← All writing
· 5 min read

Migrating Livlong to Next.js 16: the 45% LCP win

What changed when we moved livlong.com from a tangled Remix/Svelte/legacy mix to Next.js 16 — and what didn't.

Next.jsPerformanceMigration

livlong.com had four front-end modules when I joined. Two on Remix, one on Svelte, and a legacy module on something I'd rather not name. Each module had its own auth, its own component library, its own deploy pipeline.

We migrated all four to Next.js 16 with React Router v7 as the routing layer for some internal flows. The headline number was a ~45% LCP improvement. The actual win was structural.

What Next.js 16 brings that mattered to us

App Router with React Server Components. Most of our pages don't need client interactivity above the fold — they're mostly content. Moving the hero, headers, and product cards to RSC dropped the client bundle for first-load pages by a measurable amount.

Turbopack for dev. The 4× faster HMR is the kind of thing you only appreciate after a week. Then you appreciate it every minute.

Built-in font optimisation via `next/font`. We were loading 6 Google Fonts via CSS imports before. Self-hosted, subset, and preloaded automatically saved a request waterfall.

The LCP win wasn't from Next.js alone

Honest disclosure: the 45% LCP improvement came from THREE changes shipped together:

First, we moved the hero image from a CSS background to a properly sized `next/image` with `priority` and `sizes`. That alone was probably 60% of the win.

Second, RSC moved a chunk of layout work to the server, so the first paint didn't wait for the client bundle.

Third, font optimisation killed a 200ms blocking script.

Don't credit a framework for a number that came from getting the basics right. The framework made it easier; the work was still the work.

What I'd flag to anyone migrating

App Router is genuinely different. Read the relevant section of the docs before each file change. "It looks like Pages Router but with `app/` instead of `pages/`" is the road to a six-month rewrite.

Server Components compose differently. A client component cannot import a server component as a child — it can only receive one as a `children` prop. Sounds obvious; gets you twice a week.

Test the build, not just the dev server. App Router does things at build time that dev mode hides. We had three pages that worked in dev and 404'd in production. Always `npm run build` before you push.

— Saurabh