SSR vs. CSR vs. SSG: How Netflix Cut Time-to-Interactive by 50% by Shipping Less JavaScript
In 2017, Netflix's logged-out homepage was carrying about 300 kB of JavaScript. Some of it was React, some was utility libraries like Lodash, and some was the data needed to hydrate React's state. React itself was only around 45 kB of that. But when the team removed client-side React, several libraries, and the code that depended on them, the total dropped by more than 200 kB, and the page's time-to-interactive improved by over 50%. React didn't disappear: Netflix kept using it on the server to produce the initial HTML, and it prefetched React in the background so the rest of the sign-up flow, a single-page app, still loaded fast. That prefetching cut time-to-interactive by 30% on later navigations.
The lesson isn't "React is slow." It's that how a page gets built, and how much JavaScript has to run before it works, can matter more than which framework you picked. That decision is the heart of rendering strategy: do you build the HTML in the browser (client-side rendering, CSR), on the server for each request (server-side rendering, SSR), or ahead of time at build (static site generation, SSG)?
The stakes are measurable. Google's Core Web Vitals want a Largest Contentful Paint of 2.5 seconds or less, an Interaction to Next Paint of 200 milliseconds or less, and a Cumulative Layout Shift of 0.1 or less, judged at the 75th percentile of real visits. And Google's own documentation notes that the default strategy for single-page apps today is client-side rendering, while encouraging server-side rendering or pre-rendering if you're free to choose. This guide breaks down what each approach does, where the real numbers come from, and how to decide, page by page.
If you're newer to frontend work, our React & Frontend roadmap covers the fundamentals this comparison builds on.
This post originally appeared on the Ciphemic Academia blog.
The Short Version
- Client-side rendering (CSR): the server sends a nearly empty HTML page and a JavaScript bundle. The browser runs the JavaScript, fetches data, and builds the page.
- Server-side rendering (SSR): the server builds the full HTML for each request and sends it. The browser shows it right away, then JavaScript "hydrates" the page to make it interactive.
- Static site generation (SSG): the HTML is built once, at build time, and served as plain files from a CDN. Every visitor gets the same pre-built page.
If you want one default: decide per page, not per site. Use SSG for content that rarely changes, like blog posts, docs, and marketing and course pages. Use SSR for public pages that are personalized or change on every request. Use CSR for app-like screens behind a login, where search engines never look.
What All Three Are Actually Trying to Solve
Before the differences, the shared goals:
- Showing real content quickly, which Google measures as Largest Contentful Paint
- Responding fast when users interact, measured as Interaction to Next Paint
- Keeping the layout stable so content doesn't jump around, measured as Cumulative Layout Shift
- Being understood by crawlers and link previews, so search engines and social sites can read your content
- Keeping infrastructure and developer effort reasonable, since every rendering choice has a cost somewhere
The strategies differ in where and when the HTML gets built, and each answer shifts the costs to a different place.
Client-Side Rendering (CSR)
What it actually is: the browser does the rendering. The server returns a minimal HTML shell and a JavaScript bundle. The browser downloads, parses, and runs the bundle, which fetches data from an API and builds the page. A default Vite or Create React App project works this way.
A small example (what the server sends for a client-rendered app):
<body>
<div id="root"></div>
<script type="module" src="/assets/index.js"></script>
</body>
The visible content only exists after the script runs and builds it inside #root.
Where it shines:
- Rich interactivity: once loaded, navigation between screens is instant because only data moves, not whole pages
- Simple hosting: static files plus an API, with no render server to run
- Clean separation: the front end and back end can be built and deployed independently
- A natural fit behind a login: dashboards and admin tools don't need to rank in search
Where it struggles:
- A blank screen until JavaScript arrives: the user sees little until the bundle downloads, parses, and executes, which hurts Largest Contentful Paint
- JavaScript cost on slower devices: a heavy bundle can make interactions sluggish on mid-range phones, which hurts Interaction to Next Paint
- SEO risk: Googlebot can render JavaScript, but pages go through a crawl queue and then a render queue, so there's a delay, and Google notes that other search engines may ignore JavaScript entirely
- Weak link previews: social platforms often read the initial HTML for titles and images, which a bare shell doesn't contain
Who this suits: logged-in application interiors, internal tools, and anything where search visibility doesn't matter.
Server-Side Rendering (SSR)
What it actually is: the server runs your components for each request, fetches the data, and sends complete HTML. The browser can display that immediately. Then the JavaScript bundle loads and hydrates the page, attaching event handlers so it becomes interactive.
A small example (Next.js, Pages Router; other frameworks have equivalents):
// pages/courses/[slug].jsx
export async function getServerSideProps({ params }) {
const res = await fetch(`https://api.example.com/courses/${params.slug}`);
const course = await res.json();
return { props: { course } }; // runs on the server for every request
}
export default function CoursePage({ course }) {
return <h1>{course.title}</h1>;
}
Where it shines:
- Real content in the first response: users and crawlers get meaningful HTML immediately, which helps both Largest Contentful Paint and indexing
- Fresh and personalized data: every request can reflect the current database state or the current user
- Good link previews: titles and images are in the HTML
- Secrets stay on the server: API keys and database access never reach the browser
Where it struggles:
- Server cost per request: you pay compute for every page view, and a slow database makes every page slow
- Time to first byte depends on your backend, since the server has to finish rendering before it can send anything
- Hydration cost: the page can look ready before it responds to clicks, and shipping a large bundle for hydration is exactly the overhead Netflix removed from its landing page
- More infrastructure: you need a server or edge runtime, plus a caching strategy so you don't render the same page repeatedly (a cache like the ones in our Redis vs. Memcached guide often helps)
Who this suits: public pages that must be indexed and also change per request or per user, such as product pages, search results, and personalized landing pages.
Static Site Generation (SSG)
What it actually is: pages are rendered once during the build and saved as HTML files. A CDN serves those files directly, so there's no server work per visit. Many frameworks add incremental regeneration, where a page is rebuilt in the background after a set time or on demand, so static pages can stay reasonably fresh.
A small example (Next.js, Pages Router, with regeneration):
// pages/blog/[slug].jsx
export async function getStaticPaths() {
const slugs = await getAllPostSlugs();
return { paths: slugs.map((slug) => ({ params: { slug } })), fallback: 'blocking' };
}
export async function getStaticProps({ params }) {
const post = await getPost(params.slug);
return { props: { post }, revalidate: 3600 }; // rebuild at most once an hour
}
export default function BlogPost({ post }) {
return <article><h1>{post.title}</h1></article>;
}
Where it shines:
- Speed: pre-built files from a CDN are typically the fastest way to deliver a page, which supports strong Core Web Vitals
- Low cost and high resilience: no per-request compute, and static hosting keeps working under traffic spikes
- Strong SEO: complete HTML is there from the first byte
- Smaller attack surface: there's no server running your rendering code for every request
Where it struggles:
- Content freezes until the next build or revalidation: that's fine for a blog post and wrong for live inventory
- Build time grows with page count: thousands of pages can mean slow builds unless you regenerate incrementally
- Per-user content needs another route: anything personalized has to be fetched client-side or handled by a different strategy
- Rebuilds need automation: content changes should trigger builds, which is where a CI/CD pipeline, like those in our GitHub Actions vs. Jenkins vs. CircleCI comparison, comes in
Who this suits: blogs, documentation, marketing sites, and course or catalog pages whose content changes occasionally, not on every request.
Side-by-Side Comparison
| CSR | SSR | SSG | |
|---|---|---|---|
| Where HTML is built | In the browser | On the server, per request | At build time |
| First content on screen | After JavaScript runs | In the first response | In the first response, from a CDN |
| Interactivity | After the bundle loads | After hydration | After hydration (if any JavaScript ships) |
| SEO and link previews | Weakest, depends on JS rendering | Strong | Strong |
| Data freshness | As fresh as the API | Fresh on every request | As of the last build or revalidation |
| Server cost | Low (static files plus API) | Highest (compute per request) | Lowest |
| Hosting | Any static host | Needs a server or edge runtime | Any static host or CDN |
| Main risk | Slow first paint, empty shell for crawlers | Server load, hydration overhead | Stale content, long builds |
| Typical use | Logged-in apps, dashboards | Personalized or fast-changing public pages | Blogs, docs, marketing, catalogs |
How This Fits a Career Path
- Frontend and full-stack engineers: choosing a rendering strategy per route is part of building any serious site, and it shows up in code reviews and architecture discussions
- Performance-focused roles: you'll own Core Web Vitals, which means understanding how rendering, bundle size, and caching combine; our logs vs. metrics vs. traces post covers the measurement side
- SEO-aware developers: knowing what a crawler sees in the initial HTML is a practical skill that separates strong frontend engineers from average ones
- Interviews: "SSR, CSR, or SSG for this page, and why?" is a common frontend and system design question, and the best answers decide per page with reasons
How to Choose Without Overthinking It
- Ask who needs to find the page. If search engines or link previews matter, don't rely on a bare client-rendered shell. Choose SSG or SSR.
- Ask how often the content changes. Rarely, as with blog posts and docs: SSG, with regeneration if you need occasional updates. On every request or per user: SSR.
- Use CSR where search doesn't matter. Logged-in dashboards and tools are a good fit.
- Do the view-source test. Open View Page Source on a page you care about. If the article text isn't in that HTML, a crawler's first look sees an empty shell.
- Send less JavaScript regardless of strategy. Netflix's win came mainly from running less code in the browser, not from switching strategy.
- Measure real users, not your laptop. Core Web Vitals are judged at the 75th percentile across mobile and desktop, so check field data in Search Console or PageSpeed Insights.
- Don't build new sites around dynamic rendering. Google describes serving crawlers a different version than users as a workaround, not a long-term solution.
A note on honesty: the Netflix numbers describe one page, the logged-out homepage, in 2017, and engineers have pointed out that the story is as much about running less JavaScript as about any one framework. Netflix still used React on the server and for the rest of its sign-up flow. Core Web Vitals are one signal among many, and the relevance of your content matters more than shaving a few milliseconds. Thresholds can change, so check Google's current documentation. Modern frameworks also let you mix strategies within one app, so the clean categories here blur in practice.
Common Mistakes When Choosing a Rendering Strategy
- Using one strategy for the whole site. Marketing pages, logged-in screens, and personalized pages have different needs.
- Building public content pages as pure client-side apps. Blogs and course pages that need to rank shouldn't depend on a render queue.
- Assuming SSR automatically means fast. A slow backend or a huge hydration bundle can erase the benefit.
- Shipping a huge JavaScript bundle under any strategy. Bundle size drives interactivity problems everywhere.
- Forgetting Open Graph and meta tags in the HTML. Link previews read the initial response, not the post-JavaScript page.
- Lazy-loading the main above-the-fold image. That's usually your Largest Contentful Paint element, so it should load as early as possible.
- Letting content load in late and shift the layout. Reserve space for images and embeds to protect your Cumulative Layout Shift score.
- Using SSG without a refresh plan. Stale pages quietly erode trust, so decide how and when content gets rebuilt.
- Not caching SSR responses. Re-rendering identical pages on every request wastes compute.
Frequently Asked Questions
Is client-side rendering bad for SEO?
It isn't banned, since Googlebot renders JavaScript, but pages are queued for rendering, which adds delay and risk, and other search engines may not run JavaScript at all. For public content that needs to rank, server-side or static rendering is the safer choice.
What is hydration?
After the server sends HTML, the browser downloads the JavaScript and attaches event handlers and state to that existing markup, which is what makes the page interactive. Until hydration finishes, the page may look ready but not respond.
SSR or SSG for a blog?
SSG for most blogs. The content changes rarely, static files are fast and cheap, and you can trigger a rebuild when you publish. Add incremental regeneration if you need updates without a full rebuild.
Can I mix these in one app?
Yes, and most real apps do. Modern frameworks let you choose per route, so a marketing page can be static while a dashboard is client-rendered and a search page is server-rendered.
Do I need Next.js to use SSR or SSG?
No. Frameworks like Astro, Remix, Nuxt, and SvelteKit offer similar capabilities, and you can build static or server-rendered pages without a framework. Next.js is just a popular example.
How do interviewers evaluate this topic?
Usually by giving you a page and asking how you'd render it. A strong answer decides per page, names the trade-offs in speed, freshness, SEO, and cost, and mentions measuring Core Web Vitals instead of guessing.
Decide Per Page, Then Measure
The Netflix story and Google's guidance point to the same habit: choose how each page gets built based on who needs it and how fresh it must be, send as little JavaScript as you can, and check real-user numbers instead of trusting a rule of thumb. Explore the React & Frontend roadmap to practice building the same page three ways and see the trade-offs for yourself.
Top comments (0)