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