Server-Side Rendering
A full HTML document is generated on the server for every request. The user gets meaningful content instantly, and it's always fresh, at the cost of more server work per visit.
What happens
- Request,the user hits the URL. There's no cached HTML to serve.
- Render, the Node server runs the React tree top-to-bottom, awaits any data fetches, and produces a complete HTML document.
- Send, HTML is streamed to the browser. The user sees content immediately.
- Hydrate, the JS bundle arrives and React attaches handlers, making the page interactive.
The code
ssr/page.tsx
// app/rendering/ssr/page.tsx
import { Suspense } from "react";
import { connection } from "next/server";
export default function Page() {
return (
<Suspense fallback={<Skeleton />}>
<Dynamic />
</Suspense>
);
}
async function Dynamic() {
await connection(); // mark as request-time
const now = new Date().toISOString();
const token = crypto.randomUUID().slice(0, 8);
return <span>{now} · {token}</span>;
}connection() is the marker that pulls a component out of prerendering: anything that runs after it has to wait until a real visitor shows up. With Cache Components enabled, request-time work like this has to sit inside a <Suspense> boundary, so the static shell can flush first and the dynamic part streams in behind it.
Trade-offs
- Good: always fresh, can read cookies and headers, great for personalised content.
- Bad: every request burns server time; latency depends on your data sources.