Fetching Data in App Router Using Server Components, SWR, and Revalidation
Next.js 14’s App Router gives you powerful ways to fetch and cache data in both Server and Client Components. In this module, you’ll learn how to:
- Fetch data in
page.tsx(Server Components) - Use
fetch(),revalidate, and caching techniques - Perform client-side fetching using SWR and
useEffect - Compare SSG, SSR, and ISR in real-world cases
🧩 Server-Side Data Fetching with Server Components
Server Components are default in App Router. You can use async directly in page.tsx or any server-only component.
🧪 Example: Fetching Posts in src/app/posts/page.tsx
// src/app/posts/page.tsx
type Post = {
id: number;
title: string;
};
async function getPosts(): Promise<Post[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
next: { revalidate: 60 }, // ISR: revalidates every 60 seconds
});
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">Server-Fetched Posts</h1>
<ul className="space-y-2">
{posts.slice(0, 5).map((post) => (
<li key={post.id} className="p-2 border rounded">
{post.title}
</li>
))}
</ul>
</div>
);
}
⚙️ Understanding revalidate, Cache, and Fetch Options
Next.js fetch() accepts powerful options:
| Option | Description |
|---|---|
cache: "force-cache" |
Default. Uses static caching (SSG) |
cache: "no-store" |
Always fetch fresh data (SSR) |
next: { revalidate: n } |
ISR: Regenerate after n seconds |
🧪 Example:
await fetch("https://api.com/data", {
cache: "no-store" // SSR
});
await fetch("https://api.com/data", {
next: { revalidate: 10 } // ISR (refresh every 10s)
});
🧪 Static Site Generation (SSG) with Server Components
By default, page.tsx will be statically generated unless you use no-store or dynamic params. This is perfect for blog pages, marketing content, or static dashboards.
🧠 Client-Side Data Fetching
When you need real-time updates, auth tokens, or user input-dependent queries, use client-side fetching.
Option 1: useEffect + fetch
"use client";
import { useEffect, useState } from "react";
export default function ClientPosts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then(setPosts);
}, []);
return (
<div className="p-8">
<h2 className="text-xl font-bold mb-4">Client-Fetched Posts</h2>
<ul className="space-y-2">
{posts.slice(0, 5).map((post: any) => (
<li key={post.id} className="border p-2 rounded">{post.title}</li>
))}
</ul>
</div>
);
}
Option 2: Using SWR for Smart Caching
Install SWR:
npm install swr
"use client";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
export default function SwrPosts() {
const { data, error, isLoading } = useSWR(
"https://jsonplaceholder.typicode.com/posts",
fetcher
);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error loading posts</p>;
return (
<ul className="space-y-2 p-4">
{data.slice(0, 5).map((post: any) => (
<li key={post.id} className="border p-2 rounded">{post.title}</li>
))}
</ul>
);
}
⚖️ Comparing SSR vs SSG vs ISR
| Strategy | Syntax | Use Case |
| SSG | default, or fetch with force-cache |
Static pages (home, blog) |
| SSR | fetch(..., { cache: "no-store" }) |
Authenticated or fresh data |
| ISR | next: { revalidate: n } |
Semi-static content (news, listings) |
🚫 Dynamic Rendering Caveats
If using SSR (no-store) or dynamic params, your route becomes dynamic. This disables static optimization and can affect performance and edge caching.
To enforce dynamic rendering:
export const dynamic = "force-dynamic"; // Optional
✅ Summary
You now know how to:
- Use
fetch()in Server Components with caching or revalidation - Use
useEffector SWR in Client Components for real-time data - Understand and apply SSR, SSG, and ISR strategies
- Optimize performance using Next.js data-fetching best practices
🔜 Coming Up Next:
In Module 7, we’ll work with Forms and the latest Server Actions API, including validation with Zod, optimistic UI, and connecting to a database.
Would you like Module 6 in Markdown format or embedded HTML for your blog?
