Next.js Rendering Patterns
CSR, SSR, SSG, ISR, Dynamic Rendering, Static Rendering, Prerendering, Partial Prerendering, …
There have been a lot of rendering pattern names in Next.js. Some pattern names are very similar or can be used interchangeably.
This is quite confusing sometimes. So here is a memo that briefly explains the differences among these rendering patterns.
CSR
This is your good old Client-Side Rendering. In this rendering pattern, UI is rendered in the browser at run time.
React was CSR-only at first because its goal was to make user interaction on the browser easy, so it was natural to run all the rendering exclusively on the browser.
Here is a graph demonstrating how CSR works when fetching some data from an API server to render content:
SSR — Render UI on the run time server
CSR is great for rendering dynamic content, however it is bad for SEO because the actual page content is unavailable in the initial HTML response. Also, CSR produces a UX of a white blank page before finishing side effects like data fetching.
SSR (Server-Side Rendering) solves these problems by rendering the UI on the server. After doing the server rendering, the server sends the rendered HTML and JS to the browser, then the browser renders the same UI again to make the app interactive (which is called the “hydration” process).
Here is a graph that demonstrates how SSR works:
SSG — Render the UI on the build time server
CSR and SSR are good for rendering dynamic content. However, rendering static content every time at run time is inefficient and not very performant.
SSG (Static-Site Generation), a.k.a static rendering/prerendering, provides better performance for static content by prerendering the UI on the server at build time. At run time, the server only needs to serve the build time generated contentful HTML to the browser to achieve faster server response time. And just like SSR, the browser needs to hydrate to make the app interactive.
Here is a graph that demonstrates how SSG works:
ISR — SSR, then cache it
When your app has too many static pages, using SSG to render all of them at build time might make the build time significantly long.
ISR (Incremental Site Regeneration) solves this issue of SSG by doing the following:
- SSR for the first request, then cache the result on the server
- Server the cached HTML for later requests
In this way, though the first request has to wait for a bit longer because of SSR, all following requests can have SSG-like performance because they are served with the cached data.
Here is a graph that demonstrates how ISR works:
PPR — SSR + SSG on one page
Before PPR, we were only able to choose SSR or SSG on the page level (this is the same for Next.js App Router).
PPR (Partial Prerendering) solves this problem by enabling both rendering patterns on the same page. By leveraging React Server Component and SSR streaming via React Suspense, PPR can prerender static content at build time, and at request time, the Next.js server can serve the prerendered static content while generating dynamic content in the background and then stream the rest of the HTML back to the browser.
Here is a graph that demonstrates how PPR works:
It’s all about cache
However, in Next.js app router, you don’t really need to think about these rendering patterns that much. Instead, you think about “WHERE and WHEN to render WHAT part of the app, and HOW LONG to cache”.
Here is how each server rendering pattern fits in this cache explanation:
- CSR: render the whole page on the browser at run time, never cache on the server
- SSR: render the whole page on the server at request time, never cache on the server
- SSG: render the whole page on the server at build time, cache on the server until the next build
- ISR: render the whole page on the server at request time, cache on the server for
revalidate
time - PPR: render part of the app on the server at build time while rendering other parts of the app on the server at request time, cache each part of the page on the server for its
revalidate
time
A sidenote about rendering pattern naming
In Next.js, similar rendering patterns are named differently between the Pages Router and the App Router. For request time server rendering, Pages Router calls it “SSR” while App Router usually refers to it as “Dynamic Rendering”. And for build time server rendering, it’s “SSG” in Pages Router and “Static Rendering” in App Router.
(I know that Dynamic Rendering and SSR are technically different things but I don’t quite understand the difference… 😂)
Also, “SSR”, though usually refers to server-side rendering at request time, is actually a broad word, which can mean server-side rendering at any time (which can include SSG, ISR, PPR).