TutorialsNext.jsRendering & Data Fetching
Next.js

Rendering & Data Fetching

Next.js can render pages in several ways, letting you choose the right trade-off between freshness and speed per route.

The rendering strategies

  • Static (SSG) — HTML generated at build time, served from cache. Fastest. Best for content that rarely changes (marketing pages, blogs).
  • Dynamic (SSR) — HTML generated on each request. Always fresh. Best for personalized or real-time data.
  • Incremental (ISR) — static pages regenerated in the background after a time interval. Combines SSG speed with periodic freshness.

Fetching data in a Server Component

Server Components are async, so you fetch directly with await — no useEffect, no loading state plumbing:

// app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch("https://api.example.com/posts");
  const posts = await res.json();

  return (
    <ul>
      {posts.map((p) => <li key={p.id}>{p.title}</li>)}
    </ul>
  );
}

Controlling caching

Next.js extends fetch with caching options:

// Cached forever (static) — the default
fetch(url, { cache: "force-cache" });

// Never cached (dynamic) — fresh every request
fetch(url, { cache: "no-store" });

// Revalidate every 60 seconds (ISR)
fetch(url, { next: { revalidate: 60 } });
💡

Generate static params for dynamic routes with generateStaticParams() so each page is pre-built at build time:

export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((p) => ({ slug: p.slug }));
}

On-demand revalidation

After a data change (e.g., in a Server Action), purge the cache so users see fresh content:

import { revalidatePath, revalidateTag } from "next/cache";

revalidatePath("/posts");   // refresh a specific path
revalidateTag("posts");     // refresh everything tagged "posts"

Watch & Learn

A recommended video to watch alongside this chapter.

More “Rendering & Data Fetching” videos on YouTube