100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
JavaScript

Server-Side Rendering

Understand how Server-Side Rendering (SSR) generates HTML on every request in Next.js, and how it differs from static generation.

Rendering ModelsIntermediate9 min readJul 10, 2026
Analogies

What Server-Side Rendering Does

Server-Side Rendering (SSR) means Next.js generates a page's HTML on the server for every incoming request, rather than once at build time. This is essential when a page's content depends on something only known at request time -- the logged-in user's identity from a cookie, a search query string, real-time inventory levels, or geolocation from request headers. In the App Router, a Server Component becomes dynamically (per-request) rendered automatically as soon as it calls cookies(), headers(), or uses a fetch with { cache: 'no-store' }.

🏏

Cricket analogy: It's like a stadium's giant screen recalculating and displaying the exact required run rate freshly after every single ball, rather than showing a number that was printed once before the match started.

Opting Into Dynamic Rendering

Next.js infers whether a route should be dynamic based on the APIs it uses; there's usually no need for a manual switch. Calling cookies() or headers() from next/headers, reading a non-static searchParams value that affects rendering, or fetching with { cache: 'no-store' } (or a revalidate: 0 option) all signal that the output can't be reused across requests. You can also force a whole route to be dynamic explicitly by exporting const dynamic = 'force-dynamic' from the page or layout file, which is useful when a route's dynamism isn't automatically detected.

🏏

Cricket analogy: It's like a broadcaster deciding on the fly whether to cut to a fresh Snickometer replay -- the moment the umpire signals a review, the system switches from the pre-planned running order into live analysis mode.

tsx
// app/dashboard/page.tsx -- dynamically rendered because it reads cookies()
import { cookies } from 'next/headers';

async function getUserData(sessionToken: string) {
  const res = await fetch('https://api.example.com/me', {
    headers: { Authorization: `Bearer ${sessionToken}` },
    cache: 'no-store',
  });
  return res.json();
}

export default async function DashboardPage() {
  const sessionToken = cookies().get('session')?.value ?? '';
  const user = await getUserData(sessionToken);

  return (
    <div>
      <h1>Welcome back, {user.name}</h1>
      <p>Account balance: ${user.balance.toFixed(2)}</p>
    </div>
  );
}

Every visit to a fully dynamic route re-executes the entire Server Component tree for that route on the server, including all its data fetches. For high-traffic pages that don't need per-request freshness, this is significantly more expensive than SSG or ISR, both in server load and latency.

SSR vs Client-Side Data Fetching

SSR is distinct from fetching data in a Client Component with useEffect: with SSR, the HTML already contains the personalized content when it reaches the browser, so there's no loading spinner and search engines see fully rendered content. With client-side fetching, the browser first receives a mostly empty shell, then requests data and re-renders, which is slower to show meaningful content and worse for SEO on public pages, though it can feel more responsive for highly interactive, user-triggered data changes.

🏏

Cricket analogy: SSR is like handing a fan a fully filled-in scorecard the moment they sit down, versus client-side fetching being like handing them a blank scorecard they must fill in themselves by calling the scorer's box.

In the App Router, SSR and streaming with Suspense are often combined: the server can send an initial HTML shell immediately and stream in slower personalized sections as they finish, giving users the SEO and immediacy benefits of SSR without waiting for the single slowest data source.

  • SSR renders a page's HTML fresh on the server for every request, unlike SSG's build-time rendering.
  • A route becomes dynamic automatically when it uses cookies(), headers(), or a fetch with { cache: 'no-store' }.
  • const dynamic = 'force-dynamic' can explicitly force a route into per-request rendering.
  • SSR is necessary for content that depends on request-time data: sessions, personalization, real-time values.
  • SSR delivers fully rendered HTML to the browser immediately, unlike client-side fetching which starts with an empty shell.
  • SSR costs more server compute per request than SSG/ISR since there's no cached HTML to reuse.
  • SSR pairs naturally with streaming and Suspense to avoid blocking the whole page on the slowest data source.

Practice what you learned

Was this page helpful?

Topics covered

#JavaScript#NextJsStudyNotes#WebDevelopment#ServerSideRendering#Server#Side#Rendering#Does#StudyNotes#SkillVeris