We moved a large production application from the Pages Router to the App Router. It was worth doing and it was not smooth. Here is what actually happened, without the optimism of a launch post.
The good: server components change the payload maths
The headline benefit is real. Components that render on the server ship no JavaScript to the browser, and on content-heavy pages that removed a large fraction of our bundle outright.
Nested layouts were the second genuine win. Shared shells that persist across navigation without re-rendering removed a category of state-preservation workarounds we had been carrying for years.
The bad: the client boundary is contagious
A 'use client' directive applies to everything imported below it. One interactive leaf component can drag a large subtree into the client bundle, and it does so silently.
The fix is to push boundaries as far down the tree as possible and to pass server-rendered content through as children rather than importing it inside a client component. This is not obvious, and it is where most of our regressions came from.
- Mark the smallest possible component as a client component.
- Pass server-rendered nodes as children instead of importing them client-side.
- Watch for library imports that force a client boundary.
- Re-check bundle size after every boundary change.
The ugly: caching surprised us repeatedly
Caching behaviour across the router, fetches and route segments was the single largest source of confusion. Stale data appearing after a mutation was our most common bug class during the migration.
The lesson we would give our earlier selves: be explicit. State your caching and revalidation intent on every fetch and route rather than relying on defaults, and add a revalidation call to every mutation path as a matter of routine.
Would we do it again
Yes, with the caveat that the incremental path is the only sane one. The two routers coexist, so migrating route by route, highest-traffic last, let us catch problems on low-stakes pages.
If your application is small and shipping fine, there is no urgency. The benefits scale with page weight and complexity, and so does the migration cost.
Key takeaways
- Server components genuinely and substantially cut the client bundle.
- The 'use client' boundary is contagious — push it down the tree.
- Be explicit about caching; defaults caused most of our bugs.
- Migrate incrementally, highest-traffic routes last.
Ready to make the switch?
Try Zero Delay Analytics free. No credit card required, and no cookie banner needed.
Get Started