How Server-Side Rendering Actually Works in Next.js
When you open a web page, the browser needs HTML before it can show you the page.
In a traditional client-rendered React application, the browser might initially receive something like:
<div id="root"></div>Then JavaScript loads, React runs, data is fetched, and the UI is rendered.
With Server-Side Rendering (SSR), the approach is different.
Instead of making the browser build the initial page from scratch, Next.js can render the page on the server and send the resulting HTML to the browser.
Let's break down what actually happens.
1. The browser sends a request
Suppose you visit:
GET /productsThe browser sends the request to your Next.js server.
Browser
│
│ GET /products
▼
Next.js ServerAt this point, the browser hasn't rendered the products page yet. It's asking the server for it.
2. Next.js runs your server-side code
Next.js determines how the requested page should be rendered.
For a dynamically rendered page, the server can execute code such as fetching data from an API or database.
Conceptually:
Page
│
├── Fetch products
│ │
│ └── Database / API
│
└── Render React component treeFor example, your server-side code might fetch:
MacBook Pro
iPhoneand provide that data to your React components.
One important detail here is that React Server Components don't need to send their component JavaScript to the browser just to render their initial UI.
The server can execute them and produce the result needed for the page.
3. Next.js generates the HTML
Once the server has the required data and has rendered the component tree, Next.js can produce HTML for the initial page.
For example:
<h1>Products</h1>
<div>MacBook Pro</div>
<div>iPhone</div>This is the key difference from a purely client-rendered application.
The browser isn't necessarily receiving an empty container and waiting for React to construct everything.
It's receiving actual HTML containing the initial content.
4. The browser receives the HTML
The server sends the generated HTML back:
Next.js Server
│
│ HTML
▼
BrowserThe browser can now parse the HTML and display the content.
This can make the initial page useful before the entire client-side JavaScript application has finished loading.
That's particularly valuable for pages where getting meaningful content onto the screen quickly matters.
5. React hydrates the interactive parts
But what happens if your page contains a button, dropdown, form, or other interactive component?
That's where Client Components come in.
Consider:
Server Component
│
▼
Rendered HTML
│
▼
Browser
│
▼
Client Component JavaScript
│
▼
Interactive UIThe server can generate the initial HTML, but the browser still needs JavaScript for components that require client-side interactivity.
React then hydrates those parts.
Hydration essentially means React connects the necessary client-side behavior to the HTML that already exists in the browser.
For example, imagine:
<ProductList>
<AddToCartButton />
</ProductList>The product list could be rendered on the server, while AddToCartButton is a Client Component that needs JavaScript to respond to clicks.
So the user can see the initial UI, and the interactive parts become functional once their client-side JavaScript is available.
So, is Next.js always doing SSR?
No.
This is where the terminology can become confusing.
Next.js supports multiple rendering strategies.
Static Rendering
The page can be rendered ahead of time and reused for subsequent requests.
Build time
↓
Generate HTML
↓
Serve HTMLThis works well for content that doesn't need to change on every request.
Dynamic / Server-Side Rendering
The page can be rendered when a request arrives.
Request
↓
Next.js Server
↓
Fetch data
↓
Render page
↓
HTML
↓
BrowserThis is useful when the response depends on request-specific or frequently changing data.
Client-Side Rendering
You can also have components whose data fetching and rendering happen primarily in the browser.
Browser
↓
JavaScript
↓
Fetch API
↓
Render UIThis can make sense for highly interactive application experiences where SEO and initial HTML aren't the primary concerns.
Streaming
Next.js can also stream the response instead of waiting for the entire page to be ready.
Conceptually:
Server
│
├── Header ───────────► Browser
│
├── Product skeleton ─► Browser
│
└── Products ─────────► BrowserThe browser can start displaying parts of the page while the server is still working on other parts.
This becomes especially useful when some data takes significantly longer to load than the rest of the page.
The Bigger Picture
It's tempting to think of Next.js rendering as simply:
"SSR means the server renders everything."
That's not really the whole story.
Modern Next.js applications can combine different approaches within the same application.
You might have:
Next.js
│
┌───────────┼───────────┐
▼ ▼ ▼
Static Dynamic Client
Rendering Rendering Rendering
│ │ │
└───────────┼───────────┘
▼
StreamingA single page can also contain both Server Components and Client Components.
For example:
Product Page
│
├── Server Component
│ └── Fetch product data
│
├── Server Component
│ └── Render product details
│
└── Client Component
└── Add to cartThe important idea is that rendering is no longer an all-or-nothing decision.
Next.js and React can decide which work should happen on the server and which work actually needs to happen in the browser.
The Simplified Mental Model
If you remember only one flow, remember this:
Browser
│
│ Request
▼
Next.js Server
│
├── Fetch data
│
├── Run Server Components
│
├── Generate HTML
│
└── Stream response when appropriate
│
▼
Browser
│
├── Display HTML
│
└── Hydrate Client Components
│
▼
Interactive UIThat's the core idea behind modern rendering in Next.js.
The browser doesn't always have to build the initial UI from JavaScript. The server can do much of that work first, while the browser takes over where interactivity is actually needed.