New case study: a headless storefront that cut LCP from 4.8 s to 1.1 sRead the case study

Lumen Pantry · 2026

A performance rescue for a grocery subscription app

A meal-kit subscription app had grown slow enough that customers missed their weekly cut-off. In six weeks we fixed the front end, the queries and the caching behind the menu page.

  • Next.js
  • Postgres
  • Core Web Vitals
Client
Lumen Pantry
Year
2026
Role
Performance audit and remediation
Duration
6 weeks
Services
  • Performance audit
  • Next.js
  • Database tuning

The problem

Lumen Pantry customers choose their weekly box from a menu that changes every Thursday. Over two years the menu page had become the slowest page in the app: 3.9 seconds to Largest Contentful Paint on mobile, with a 2.2 MB JavaScript bundle. On Wednesday evenings, close to the order cut-off, the API p95 reached 4.1 seconds and support tickets about failed changes tripled.

The in-house team of four was busy with a new pricing model and could not stop to investigate. Each guess they had tried, such as a larger database instance, helped for a week. They asked us for a fixed six-week engagement: find the causes, fix the worst ones alongside them, and leave a way to keep it fast.

Process

  1. 01

    Measure the real bottlenecks

    We added tracing to the API and real-user monitoring to the front end before changing anything. Two findings stood out. The menu endpoint ran 140 queries per request, a classic N+1 from loading each recipe's allergens separately. And the menu page shipped the entire account area's JavaScript because of one shared layout import.

  2. 02

    Fix queries, then cache

    We rewrote the menu query as two joins with the right composite index, taking it from 140 queries to 3 and from 1.8 seconds to 90 ms. Only then did we add caching: the weekly menu is identical for everyone, so it renders as a static page revalidated when the menu team publishes. Personal selections load in a small client request.

  3. 03

    Cut the bundle, guard it

    We split the layout so account code loads only on account pages, replaced a 300 KB date library with native Intl calls, and lazy-loaded the recipe video player. The bundle went from 2.2 MB to 410 KB. We left a bundle-size check and a Lighthouse run in CI with agreed budgets, so regressions show up in review.

01Menu page LCP before and after, p75 mobile field data
02N+1 allergen lookups replaced with two joins and a composite index
03Bundle analysis: 2.2 MB down to 410 KB after splitting the layout
04Weekly menu as a static page, personal picks loaded on top

Outcome

By the end of week six, mobile LCP on the menu page was 1.3 seconds and the Wednesday p95 held at 220 ms under peak load. Support tickets about failed box changes dropped by 81% in the following month. The team still runs the budgets we set and has not had to raise them.

1.3 s
menu LCP, down from 3.9 s
220 ms
API p95 at Wednesday peak
−81%
failed-change support tickets
Live demo · Northbound Works— View template →