What are the limitations of Next.js?
Next.js is a strong default for most React apps, but it has trade-offs worth knowing before you commit.
Framework lock-in — moving off Next.js later is non-trivial. File-based routing, Server Components, Server Actions, and the caching model all tie you to the framework. Plain React or Vite apps are easier to migrate.
Steep learning curve in the App Router — Server vs Client Components, the caching layers, streaming with Suspense, Server Actions, async params — there's a lot to internalize before you're productive. The mental model is different from vanilla React.
Caching complexity — the Full Route Cache, Router Cache, Data Cache, and request memoization interact in subtle ways. Stale content and cache-invalidation bugs are a common source of production surprises.
Build times on large sites — projects with thousands of static pages can take minutes to build. ISR and on-demand revalidation help, but cold builds still grow with page count.
Self-hosting friction — Vercel is frictionless, but matching feature parity on your own infrastructure (ISR, image optimization, middleware, edge functions) requires work. Not every Next.js feature runs cleanly on every platform.
Cold starts on serverless — when deployed as serverless functions, dynamic routes can have noticeable cold-start latency, especially with large dependency graphs. Edge runtime helps but has its own limitations.
Edge runtime constraints — middleware and Edge routes run on a V8-isolate runtime, not Node. Many npm packages don't work (anything using Node core modules, native bindings, or large file I/O).
Bundle size of the runtime — the Next.js client runtime is heavier than a hand-built React+Vite setup, particularly for small pages where the framework overhead dominates.
Rapid breaking changes — the App Router has been evolving fast. Patterns recommended a year ago sometimes shift. Version 15's caching-default inversion was a meaningful migration.
Non-trivial Docker images — without
output: 'standalone', production images bundlenode_modulesand can get large.standaloneis great but it's an extra config step.Limited SSE and long-lived connections — Server-Sent Events and WebSocket-style long-lived connections don't fit serverless deployments well. You usually need a separate service for them.
React-only — if you want Vue, Svelte, or Solid, Next.js isn't for you.
When Next.js is probably the wrong choice:
Embedded widgets or component libraries — use Vite library mode or tsup.
Pure SPA dashboards behind auth where SEO doesn't matter — Vite+React Router is simpler.
Content sites that want minimal JS — Astro ships less JavaScript and is optimized for static content.
Real-time-heavy apps — a dedicated Node server (Fastify, NestJS) with WebSocket support may fit better.
For most product apps — landing pages, authenticated UI, some API routes, SEO matters — Next.js is still the path of least resistance despite these tradeoffs.
Be honest about limitations — it shows maturity. Key ones: Vercel-centric features, learning curve, middleware Edge Runtime restrictions.
Balance by mentioning how these tradeoffs are often worth it for the benefits.