
SSR vs CSR vs SSG vs ISR: Which Rendering Pattern Should You Use?

Pick the wrong rendering strategy and you will feel it. Your Lighthouse scores tank, your build pipeline takes 45 minutes, or your users on budget Android phones stare at a blank white screen for three seconds before anything shows up. The choice between SSR vs CSR vs SSG vs ISR shapes every one of those outcomes, and most explanations of these patterns skip the part that actually matters: when does each one hurt you?
Here is a breakdown of all four, plus the classic server-rendered approach that predates all of them, with real trade-offs for each.
The Pipeline Every Rendering Pattern Shares
Before comparing the patterns, you need to understand the three stages every page goes through. Each acronym in this space is really just a decision about where to spend your time across these stages.
Stage 1: Build time. This is when you compile, transpile, and bundle your source code. For most approaches, this is fast. For static site generation, this is where the heavy lifting happens.
Stage 2: Server request time. When a user hits a URL, what does your server actually do? Does it compute a full HTML response from scratch? Does it pull a pre-built file from a cache? Does it do nothing at all? This stage has enormous consequences for page load performance and Time to First Byte (TTFB), which directly feeds into your Core Web Vitals scores.
Stage 3: Client time. Once the browser gets something, how much work does it still have to do? Parsing JavaScript, running framework code, fetching data, and the process of connecting server-rendered HTML to client-side JavaScript (called hydration, or sometimes rehydration) all live here.
Every rendering pattern makes a different bet about where to put the work. None of them is universally correct.
SSR vs CSR vs SSG vs ISR: What Each Pattern Actually Does
Classic Server Rendering (The Old Baseline)
Before React, before any of this, the web worked like this: a user requests a page, your server builds the full HTML response (database queries and all), and ships it down. The client does almost nothing except paint what it receives.
Build time: tiny. Server time: heavy. Client time: near zero.
The server handled everything. If your server was slow or under-loaded, users waited. This is still how PHP, Rails, and Django apps work by default, and honestly, for many use cases, there is nothing wrong with it.
Client-Side Rendering (CSR)
CSR flips the model. Your server sends down a nearly empty HTML shell and a bundle of JavaScript. The browser then runs that JavaScript, which builds the entire page in the client.
This is what you get out of the box with a standard React app. If you view the source of a plain React project, you will see something like a single <div id="root"></div> and a handful of script tags. There is no actual content in the HTML.
The upside: You do not need a real server. You can host on a CDN or any static host for almost nothing.
The downside: You are pushing all the work onto the user's device. Someone on a mid-range phone from a few years ago has to download your JavaScript bundle, parse it, execute it, and then fetch data before they see anything useful. That is a lot of client workload distribution onto hardware you do not control. It also creates real SEO problems: search crawlers that do not fully execute JavaScript will index an empty page.
Server-Side Rendering (SSR)
SSR is a modern take on the old server-rendered approach, but with a key difference: your server generates the full HTML on each request, then the browser receives actual content immediately. After that, a process called hydration connects the static HTML to your JavaScript framework (Next.js and Remix do this, for example), which adds a small amount of client-side work before the page becomes fully interactive.
The result is a better experience than CSR. The user gets meaningful HTML quickly, which improves Largest Contentful Paint (LCP) and helps with SEO because the full page content is in the initial response. It also handles dynamic data naturally: user authentication, personalized dashboards, live inventory counts. All of that works because the server generates the page fresh on every request.
The trade-off is server workload management. Every request hits your server and triggers computation. Under high traffic, that adds up. You either pay for more server capacity or you accept slower response times.
Static Site Generation (SSG)
SSG moves all the work to build time. During your build step, your framework generates a complete, pre-built HTML file for every page in your application. When a user requests a page, the server (or CDN) hands back a file it already has. No computation. No database query. Just a file transfer.
Kyle's blog, built with Astro, works this way. With around 100 to 200 articles, his build takes one to two minutes. The payoff is that every page load is nearly instant because it is just serving a cached HTML file from a CDN edge node close to the user.
The benefits of static site generation are real and measurable. Hosting is cheap, scaling is trivial (a CDN handles it), and page load performance is about as good as it gets.
But SSG has a hard limitation: it cannot handle dynamic content well. User authentication, personalized feeds, anything that changes between requests cannot be statically pre-built. If your content is relatively stable (a blog, a marketing site, a documentation site), SSG is excellent. If users log in and see different things, it is the wrong tool.
There is also a build time problem at scale. An e-commerce site with 50,000 products and 10 size/color variants each could have 500,000 pages to pre-render. A two-minute build becomes a four-hour build, and you run that every time a product price changes.
Incremental Static Regeneration (ISR)
ISR is Next.js's answer to the scale problem with SSG. The short version: you statically generate the pages you can at build time, and for the rest, you generate them on demand and cache the result.
So what is incremental static regeneration in Next.js, practically speaking? You configure a revalidation window for a page (say, 60 seconds). The first request after that window expires triggers a background rebuild. The requesting user might get the slightly stale version while the new one is being generated, and every subsequent request gets the fresh cached version. Some configurations will block and wait for the fresh build; others serve stale-while-revalidate. It depends on your setup.
This makes ISR a good fit for e-commerce sites with large catalogs or sites connected to a CMS like WordPress, where content changes regularly but not constantly. You get fast responses from the cache most of the time, and the data stays reasonably fresh without rebuilding everything on every request.
Server workload is lower than pure SSR but not zero (pages that need regeneration still require computation). Build time is shorter than pure SSG (you are not pre-generating everything). It sits neatly in between.
Picking the Right Pattern for Your Project
The question of when to use static site generation vs server-side rendering comes down to one thing: how dynamic is your data, and how much does that dynamism matter to your users?
Here is a rough mental model:
- Content that never changes between requests (blog posts, docs, marketing pages): SSG. Ship it from a CDN and be done with it.
- Content that changes occasionally (product listings, CMS-driven pages, article feeds): ISR. Get the cache benefits without rebuilding the world every time.
- Content that is personalized or changes every request (dashboards, authenticated pages, live data): SSR. You need the server to know who is asking.
- Prototypes, internal tools, or apps where SEO is not a concern: CSR is fine. It is the quickest way to get something up without a backend.
For production apps that mix concerns (and most do), the real answer is "it depends on the route." Next.js lets you mix these patterns per page, which is part of why it became popular. A marketing homepage can be SSG. A product listing page can use ISR. A user dashboard can be SSR. That flexibility matters more than picking one pattern for the whole app.
One common mistake is defaulting to CSR because "React just does that." Plain React does, but you are giving up SEO and first-load performance for no real benefit on most pages. Switching to SSR for public-facing pages is usually worth the small infrastructure complexity.
The rendering performance trade-offs really come down to where you can afford slowness: in your build pipeline, on your server, or on your user's device. Slow builds are a developer problem. Slow servers are a cost problem. Slow clients are a user problem. Prioritize accordingly.
For a deeper technical breakdown of how these patterns behave in practice, the Next.js rendering documentation is worth reading alongside any framework you choose. It covers the nuances of how React's server components interact with these strategies in ways that tutorials often gloss over.
Frequently Asked Questions
What is the main difference between SSR and CSR for performance?
With SSR, the server sends a fully populated HTML page, so the browser can start displaying content immediately. With CSR, the browser gets an empty shell and has to run JavaScript before anything shows up. For users on slow connections or slower devices, that difference is noticeable. Server-side rendering vs client-side rendering performance is not always dramatic on fast hardware, but it shows up in real-world Core Web Vitals data, especially LCP.
When should I use SSG over SSR?
If your content does not change between requests and does not need to be personalized per user, SSG will always be faster and cheaper than SSR. A documentation site, a blog, a landing page: all of those are natural fits. But honestly, the moment you need to show anything user-specific, SSG alone cannot do it without client-side fetching as a workaround, which partially defeats the point.
What is the best rendering pattern for e-commerce sites?
It genuinely depends on the catalog size and update frequency. For smaller shops with stable inventory, SSG is hard to beat. For large catalogs where prices and stock change frequently, ISR is usually the better call. Fully dynamic product pages (like ones with real-time inventory counts or personalized pricing) might need SSR for those specific routes. Most production e-commerce setups mix patterns per page type rather than committing to one globally.
Does CSR hurt SEO?
It can, yes. Googlebot does execute JavaScript, but there is a processing delay and crawl budget considerations. Other search engines are less reliable about it. If SEO matters for your pages, sending real HTML from the server (via SSR or SSG) is the safer choice. CSR is fine for app interiors that are behind authentication and would not be indexed anyway.
Is ISR available outside of Next.js?
The concept exists elsewhere under different names. Gatsby calls a similar feature "Deferred Static Generation." Some hosting platforms implement their own edge caching with revalidation that achieves similar results. But ISR as a first-class, built-in feature is most mature and well-documented in the Next.js ecosystem, which is part of why it gets associated with the framework specifically.
Get CodeTips in your inbox
Free subscription for coding tutorials, best practices, and updates.