What We Learned Migrating a SaaS Product to Next.js
Server actions, streaming, and the handful of gotchas that cost us a week — so you don't lose yours.
We recently migrated a 40k-line SaaS app from a custom Express + CSR React setup to Next.js App Router. It was the right call — but the migration ate a week we didn't plan for. Here's what we learned, so you don't lose yours.
Server Actions are great — until they're not
Server Actions feel like magic until you hit an edge case where you need progressive enhancement or a custom error boundary. We ended up using them for ~70% of mutations and kept a thin API layer for everything that needed to be called from outside the React tree (webhooks, cron jobs, mobile). Don't force everything into Actions.
Streaming + Suspense changes how you think about loading
The old pattern was a spinner while data loads. With streaming, you ship the shell instantly and stream sections as they resolve. This is better for perceived performance, but it means your components need to handle their own loading and error states independently. Plan your Suspense boundaries upfront — retrofitting them is painful.
The gotcha that cost us a week: client components
Not every component that uses state needs to be a client component. We over-marked early on and ended up shipping a much larger JS bundle than the old CSR app. The rule: only mark a component 'use client' if it uses hooks, event handlers, or browser APIs. Keep the rest as server components and pass data down.
Caching is a different mental model
Next.js's fetch caching is powerful but unintuitive. We shipped a bug where users saw stale data for 5 minutes because we forgot to revalidate a tagged cache. Tag your caches explicitly, set sane revalidate windows, and use on-demand revalidation for anything that mutates.
Net result: 3x faster first load, half the JS, and a codebase that's easier to reason about. The migration was worth it — just budget more time than the docs suggest, and read the edge cases before you start.
Want thinking like this on your project?
We don't just write about this stuff — we do it, every day, for brands like yours.
Start a project