Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Rendering Strategies  ›  Lesson

Server-Side Rendering

SSR 14 minServer-side rendering builds a page's HTML on the server for each request, so it can show fresh, per-visitor data, and Next.js does this automatically when a page reads request-time information.

Server-Side Rendering

Trailhead's trail list can be filtered: /trails?difficulty=easy shows only easy trails, and the page greets a signed-in hiker by name. No one can prepare that page in advance, because it depends on the exact request. So the server builds it fresh, each time someone asks.

The rule

Needs the request? Then render per request, on the server.

With server-side rendering (SSR), also called dynamic rendering, the server runs your components when a request arrives, sends complete HTML, and the browser then hydrates it. Visitors see content immediately, and it can be specific to them.

In the App Router you rarely switch this on by hand. Next.js renders a route dynamically when it uses information that only exists at request time:

  • the page's searchParams (the query string),
  • cookies() or headers() from next/headers,
  • a fetch that opts out of caching with { cache: "no-store" }.
// app/trails/page.tsx
import { cookies } from "next/headers";
import { getTrails } from "@/lib/trails";

export default async function TrailsPage({
  searchParams,
}: {
  searchParams: Promise<{ difficulty?: string }>;
}) {
  const { difficulty } = await searchParams;
  const hiker = (await cookies()).get("hiker")?.value;
  const trails = await getTrails({ difficulty });
  return (
    <>
      <p>Welcome back{hiker ? `, ${hiker}` : ""}</p>
      <ul>{trails.map((t) => <li key={t.slug}>{t.name}</li>)}</ul>
    </>
  );
}

Like params, searchParams and cookies() are asynchronous in current Next.js, so you await them.

You can also force it for a whole route with export const dynamic = "force-dynamic", but letting Next.js detect it is usually cleaner.

Costs: the server does work on every visit, and the response cannot start until the data it waits for is ready. A slow database makes every page view slow. Streaming, covered later, softens this.

The shortcut

Scan the page for request-time inputs: searchParams, cookies, headers, no-store fetches. Find one and the route is dynamic; find none and it can be prepared ahead of time.

Is /trails rendered per request?Step 1 of 4
  1. Scan the props

    It awaits searchParams for the difficulty filter. That is a request-time input.

The trap is reading a cookie deep in a shared component "just for a greeting". It quietly makes every page that uses the component dynamic. Put request-specific bits where you mean them to be.

Drills

Answer fast — trust the rule.

quick drill

What does the visitor's browser receive first from a server-rendered page?

quick drill

Which of these makes a Next.js route render per request?

quick drill

How do you read the query string in an App Router page?

quick drill

What is the main cost of rendering a page per request?

Remember

  • Needs the request? Render per request, on the server, sending finished HTML.
  • Shortcut: scan for searchParams, cookies, headers or no-store fetches.
  • Trap: a request-time read hidden in a shared component makes every page dynamic.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainWhy this page is dynamic

Trailhead's /trails page reads searchParams to filter by difficulty, and it reads a cookie to greet the signed-in hiker. Explain why Next.js renders this route on every request, what the visitor receives, and one cost of that choice.