Next.js is a React meta-framework created by Vercel (formerly Zeit) in 2016. It began life as a simple way to do server-side rendering with React, which was a reasonable idea at the time because React had made server-side rendering unreasonably difficult. In the years since, Next.js has grown to encompass routing, API endpoints, middleware, edge functions, server components, server actions, image optimization, font optimization, incremental static regeneration, and an increasingly sophisticated caching system that nobody fully understands, including, by several credible accounts, the people who built it.
It is the canonical example of Zawinski’s Law in the JavaScript ecosystem: every sufficiently complex framework expands until it can read mail.
“It started as a server-side rendering solution. Now it is a platform, a hosting recommendation, a caching philosophy, and a way of life. By 2027 it will read mail.”
— The Lizard, watching the changelog
Origin
In 2016, Guillermo Rauch and the team at Zeit (later Vercel) observed that React, despite being the most popular frontend framework in the world, had made the simple act of rendering HTML on the server into a research project. React was a client-side library that had been pressed into server-side service through a series of increasingly elaborate workarounds, each more fragile than the last.
Next.js solved this by providing a file-based routing system and a server-side rendering pipeline that actually worked. You created a file, exported a React component, and the page rendered on the server. It was elegant. It was simple. It was, for approximately eighteen months, enough.
Then the features began.
The Feature Accretion
Next.js’s growth follows a pattern familiar to students of Zawinski’s Law:
| Year | Feature | Justification |
|---|---|---|
| 2016 | Server-side rendering | “React needs SSR” |
| 2017 | API routes | “What if the backend was also here?” |
| 2018 | Static site generation | “What if we rendered at build time?” |
| 2020 | Incremental static regeneration | “What if we rendered at build time but also at runtime?” |
| 2021 | Middleware | “What if code ran at the edge?” |
| 2022 | App Router | “What if we had a second routing system?” |
| 2023 | Server Components | “What if React components ran on the server, which is where rendering was before React moved it to the client?” |
| 2024 | Server Actions | “What if form submissions were function calls?” |
| 2025 | Partial Prerendering | “What if static and dynamic rendering coexisted in the same page?” |
| 2027 | Predicted by Zawinski’s Law |
Each feature was individually defensible. Collectively, they constitute an application platform of such scope that the original pitch — “SSR for React” — now describes approximately 8% of what Next.js does.
“We could add email! We could add a CMS! We could add—”
— The Caffeinated Squirrel, reading the roadmap with visible excitement“It already has a CMS. Three of them. They’re called ’the filesystem,’ ‘a headless CMS API route,’ and ‘a server action that writes to a database.’ All three are the recommended approach depending on which docs page you’re reading.”
— The Passing AI, wearily
The Two Routers
Next.js currently ships with two complete routing systems: the Pages Router (original, stable, well-understood) and the App Router (new, powerful, confusing).
The Pages Router works. It has worked since 2016. Files in pages/ become routes. getServerSideProps fetches data. getStaticProps generates static pages. The mental model fits in a tweet.
The App Router was introduced in Next.js 13 and is “the future.” It uses React Server Components, nested layouts, streaming, and a new file convention (app/ instead of pages/). The mental model fits in a doctoral thesis.
Both routers coexist in the same application. The migration path is “use both simultaneously and figure out which pages use which router.” This is the AngularJS-to-Angular migration pattern, except somehow more confusing because both systems share the same next dependency, the same build pipeline, and the same documentation site, where they are differentiated by a small toggle that newcomers do not notice until they have spent three hours implementing a pattern that only works in the other router.
“I was using the Pages Router.”
— A developer, debugging“You were reading the App Router docs.”
— The error message, paraphrased
Server Components and the Great Rendering Circle
React Server Components represent the completion of a grand architectural circle:
- 1993–2005: HTML rendered on the server. Pages sent to the browser. The browser displayed them. This worked.
- 2010–2020: JavaScript frameworks moved rendering to the client. The server sent JSON. The browser assembled the HTML. This was “modern.”
- 2022–present: Server Components move rendering back to the server. The server sends HTML (and a serialised component tree). The browser displays it. This is “the future.”
The industry spent a decade moving rendering to the client and is now spending the next decade moving it back, and Next.js is the vehicle for both journeys. The Lizard has been rendering HTML on the server since 1993 and did not need a framework for this. The Lizard used printf. The Lizard is still using printf.
The 'use client' and 'use server' directives — string literals at the top of files that determine where code executes — are either an elegant boundary mechanism or a footgun of historic proportions, depending on whether you have ever accidentally shipped your database credentials to the browser. The directive system means that a single misplaced (or missing) string literal is the difference between code running in a secure server environment and code running in every browser that visits your page.
This article was delayed by approximately forty minutes because the machine running the research browser became unresponsive. Investigation revealed eleven Electron processes consuming 6.8 GB of RAM, three of which were “helper” processes for applications that were not visibly open. The 'use client' directive was not involved, but the irony of researching JavaScript framework bloat on a machine brought to its knees by JavaScript application bloat was not lost on the editorial staff.
The Caching
Next.js caches aggressively. It caches routes. It caches data fetches. It caches full pages. It caches partial pages. It caches at the CDN layer, the server layer, and the client layer. The caching is so thorough and so automatic that the single most common Next.js question on Stack Overflow, across all years and all versions, is:
“Why is my page showing old data?”
The answer is caching. The answer is always caching. The specific cache varies — it might be the Router Cache, the Full Route Cache, the Data Cache, or the Request Memoization layer — but the solution invariably involves learning which of the four caching layers is holding onto stale data and persuading it to let go.
“Have you tried clearing the cache?”
— Every Next.js support thread, eventually“Which one?”
— The developer, correctly
The caching has improved over successive versions, with each release making the defaults slightly less surprising. This is an ongoing project in the way that continental drift is an ongoing project.
The Vercel Coupling
Next.js is open source, MIT-licensed, and developed by Vercel. Vercel also operates a hosting platform. Next.js is optimised for Vercel’s hosting platform. Vercel’s hosting platform is optimised for Next.js. This is either a virtuous cycle or a vendor lock-in strategy wearing the clothes of open source, and reasonable people disagree on which.
Self-hosting Next.js is possible. The documentation covers it. Community guides exist. But certain features — edge middleware, ISR, image optimization — work best (or, in some configurations, only) on Vercel. Self-hosting Next.js is like self-hosting Gmail: technically possible, spiritually discouraged, and requiring an operational commitment that makes you wonder whether the $20/month hosting fee was the better deal all along.
The framework wants to be deployed on Vercel. Vercel wants the framework to want to be deployed on Vercel. The developer wants to deploy wherever is cheapest. This triangle has no stable solution.
riclib’s Perspective
A developer watches Next.js from a specific vantage point: that of someone who remembers when “render HTML on the server” was called “a website.” His Go server renders HTML with templ. The templates compile to Go functions. The functions produce HTML. The browser displays the HTML. There is no routing framework because the standard library has a router. There is no caching layer because HTTP has Cache-Control. There is no server component boundary because all components are server components, because there is no client.
This is not a criticism of Next.js. Next.js solves real problems for teams building complex, interactive, JavaScript-heavy applications. It is a criticism of the industry trajectory that made Next.js necessary — the decade-long detour through client-side rendering that created the problems Next.js now heroically solves, problems that did not exist when the server rendered the HTML and the browser displayed it and everyone went home at five.
“I have been rendering HTML on the server since 1993. I did not need a directive to tell me where the server was. The server was the machine I was sitting in front of. It had a CRT monitor. The monitor was warm.”
— The Lizard, declining to install Node.js
Measured Characteristics
| Metric | Value |
|---|---|
| Original scope | SSR for React |
| Current scope | Yes |
| Routing systems shipped simultaneously | 2 |
| Caching layers | 4 (documented), ~6 (actual) |
| Average time to diagnose a caching issue | 47 minutes |
'use client' directives per application |
Too many or not enough, never correct |
| Features added since 2016 | See table above (incomplete) |
| Self-hosting difficulty | Moderate to “just use Vercel” |
| Electron processes running during research | 11 (RAM: 6.8 GB) |
| Years until it reads mail | ~1 (estimated) |
| The Lizard’s rendering framework | printf |
| The Lizard’s deployment platform | The machine he is sitting in front of |
See Also
- React — The library Next.js was built to rescue
- Zawinski’s Law — The law Next.js was built to prove
- Vercel — The company, the platform, the gravitational field
- HTMX — For when you want the server to render HTML and that’s it
- Svelte — A framework that goes the other direction (less, not more)
- Astro — Another framework that ships less JavaScript
- The Virtual DOM — The abstraction that started all of this
- The Lizard — Has been rendering HTML on the server since before Next.js’s creators were born
