SyntaxStudy
Sign Up
Next.js Server-Side Rendering and Dynamic Routes
Next.js Beginner 1 min read

Server-Side Rendering and Dynamic Routes

Server-Side Rendering (SSR) generates the HTML for each page on every request. In the App Router, you opt a page into SSR by using dynamic functions like cookies(), headers(), or by passing { cache: 'no-store' } to fetch(). When Next.js detects these signals it marks the route as dynamic and renders it fresh for every request, ensuring users always see the latest data. The dynamic export constant offers an explicit opt-in or opt-out. Setting export const dynamic = 'force-dynamic' makes the entire route dynamic regardless of what fetch options you use. This is useful when you want to be explicit about rendering mode. Conversely, export const dynamic = 'force-static' suppresses all dynamic signals — cookies(), headers(), and searchParams all return empty values, and the route is rendered statically. Dynamic rendering adds latency because the HTML must be generated before any content is shown. Next.js mitigates this with streaming: it begins sending the HTTP response headers immediately and streams HTML chunks as they become available. Wrapping slow components in Suspense boundaries allows fast content to stream first, so users see meaningful content without waiting for the slowest data source.
Example
// app/feed/page.tsx — fully dynamic (SSR) page

import { cookies, headers } from 'next/headers';

// Opt this entire route into dynamic rendering
export const dynamic = 'force-dynamic';

async function getFeed(userId: string) {
  // no-store = skip cache, always fetch fresh
  const res = await fetch(`https://api.example.com/feed/${userId}`, {
    cache: 'no-store',
  });
  if (!res.ok) throw new Error('Failed to load feed');
  return res.json();
}

export default async function FeedPage() {
  // Reading cookies opts the route into dynamic rendering
  const cookieStore = cookies();
  const userId = cookieStore.get('userId')?.value;

  // Reading headers also opts into dynamic rendering
  const headersList = headers();
  const userAgent = headersList.get('user-agent') ?? 'unknown';

  if (!userId) {
    return <p>Please log in to see your feed.</p>;
  }

  const feed = await getFeed(userId);

  return (
    <div>
      <p className="text-xs text-gray-400">Viewing from: {userAgent}</p>
      <ul>
        {feed.items.map((item: { id: string; title: string }) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
}