The problem
Bayleaf's storefront was a bought theme with eleven plugins bolted on. Mobile LCP sat at 4.6 seconds, the category pages shipped 2.3 MB of JavaScript, and the checkout abandoned 71% of carts on phones.
What I built
A Next.js App Router front end against the existing commerce API, deployed on Vercel:
- Statically generated category and product pages, revalidated on webhook when stock or price changes.
- A cart that lives in a server action plus a cookie, so it survives a refresh without a client-side store.
- Search with Redis-backed suggestions, debounced and cancelled properly, so typing fast does not queue eleven requests.
- A three-step checkout with Stripe Payment Element, inline validation and no page reloads.
Decisions worth explaining
Keep the backend. Replacing the commerce engine would have been a year-long project. Treating it as an API and replacing only the front end took eleven weeks and shipped value in week three.
Images are the budget. Every product image goes through the Next.js optimiser with explicit dimensions, AVIF first. That single change accounted for roughly half the performance win.
No skeleton screens on static pages. If a page is prerendered, the content is already there. Skeletons on static content are an animation that makes a fast page feel slow.
Results
- Mobile LCP 4.6 s → 1.1 s; Lighthouse performance 34 → 97.
- Mobile checkout completion up 22% over the following quarter.
- Content editors publish a new category themselves, with no developer in the loop.