Frontend · Guide

Server Rendering

SSR, static generation and incremental rendering, server components, and loading data — why a meta-framework exists at all.

— min read Frontend

Why Render on the Server At All

A pure client app ships an empty page and a large bundle. Nothing is visible until the JavaScript downloads, parses and runs — and search crawlers and slow phones both pay that bill.

Server rendering sends real HTML. The user sees content on the first response instead of after a round of downloading and executing, and a crawler gets a page rather than an empty shell.

The page then hydrates: the same components run in the browser and attach their event handlers to markup that already exists. Between arrival and hydration the page looks ready but does not respond — a real gap, and a large bundle widens it.

This is most of what a meta-framework is for. Routing, data loading, bundling and the server and client halves of rendering are hard to wire together correctly, and the framework has made those decisions already.

SSR, SSG & Incremental Rendering

The real question is when the HTML is produced. Everything else follows from that answer.

ModeHTML is builtFitsCost
CSRIn the browser, after JS loadsDashboards behind a loginBlank first paint, poor for crawlers
SSRPer request, on the serverPersonalised or fast-changing pagesServer cost and latency on every hit
SSGAt build timeDocs, marketing, blogsStale until you rebuild
ISRAt build, then refreshed in the backgroundCatalogues that change sometimesA window where content is stale

Prefer the earliest one that works. Static HTML from a CDN is the fastest and cheapest thing on the web; per-request rendering is the most flexible and the most expensive. Incremental regeneration is the middle: serve the cached page, rebuild it behind the scenes when it ages out.

These are per-route decisions, not per-application. A marketing page can be static while the account page beside it renders per request.

Server Components

A server component runs only on the server and never ships to the browser. Its output is sent as rendered markup, so the code — and any dependency it used — stays out of the bundle entirely.

That is the appeal: a component that formats dates with a large library adds nothing to what the user downloads. Data fetching can also happen directly inside it, with no API round trip from the client.

Server componentClient component
Ships JS to the browserNoYes
State and effectsNoYes
Event handlersNoYes
Direct database accessYesNo
Browser APIsNoYes

The two compose: server components render the static shell and pass data down; client components handle anything interactive. Push the boundary as deep as you can — a single interactive button should not make its whole page a client component.

Anything a server component touches — secrets, tokens, database handles — stays on the server. That is exactly why it must never be passed as a prop into a client component, where it would be serialised and shipped.

API Routes & Data Loading

With rendering on the server, data loading moves there too — fetch during render rather than after mount, and the HTML arrives complete instead of arriving empty and filling in.

The failure to watch for is the waterfall: a parent awaits its data, then renders a child that awaits its own. Two sequential round trips where one would do. Start independent requests together and await them together.

SituationDo
Several independent fetchesStart them in parallel, await once
One slow sectionStream it behind a Suspense boundary
Data that rarely changesCache it; revalidate on a timer
A user action changing dataMutate on the server, then revalidate
Anything secretKeep it server-side; never send it down

A meta-framework also gives you API routes — server endpoints living beside the pages. Useful for webhooks, form handling and anything needing a key you cannot expose, without standing up a separate service.

Streaming is the practical win. Send the shell immediately and let slow sections arrive as they resolve, so one slow query stops holding the entire page hostage.

Interview Questions

SSR versus SSG — how do you choose?

By when the HTML can be built. If the page is the same for everyone and changes rarely, build it once at deploy and serve it from a CDN. If it is personalised or changes constantly, render per request and pay the server cost. Incremental regeneration sits between them.

What is hydration, and what goes wrong with it?

The client re-runs the components and attaches handlers to server-rendered markup. Between paint and hydration the page looks interactive but is not, and a large bundle widens that gap. A mismatch between server and client output also forces a re-render and can flash.

What does a server component actually save?

Bundle size and a round trip. The component and its dependencies never reach the browser, and it can read data directly instead of calling an API. The trade is no state, no effects, no event handlers.

What is a data waterfall?

Sequential fetches that could have been parallel — a parent awaits, then renders a child that awaits. Each layer adds a full round trip. Start independent requests together.

Why not render everything on the server?

Anything interactive needs client JavaScript, and per-request rendering costs server time and latency on every hit. The aim is the smallest client bundle that still delivers the interactivity the page needs.

Quick Quiz

1. A marketing page identical for everyone is best served by…
2. Between first paint and hydration the page is…
3. A server component cannot…
4. A parent awaits data, then its child awaits more. That is…
5. Rendering mode is chosen…