Data Fetching in the App Router
Forget getServerSideProps and getStaticProps. Those are Pages Router (old). In the App Router, you just... fetch data. In your component. Because Server Components can be async.
Basic Fetching
1// src/app/posts/page.tsx2// This component runs on the server, fetch happens server-side34interface Post {5id: number;6title: string;7body: string;8}910export default async function PostsPage() {11const res = await fetch("https://jsonplaceholder.typicode.com/posts");12const posts: Post[] = await res.json();1314return (15 <ul>16 {posts.map((post) => (17 <li key={post.id}>18 <h2>{post.title}</h2>19 <p>{post.body}</p>20 </li>21 ))}22 </ul>23);24}No useEffect needed
Caching Behavior
Next.js 15 changed the caching defaults. Fetch requests are NOT cached by default anymore (they were in v14). You now opt-in to caching:
1// No caching (default in Next.js 15)2const data = await fetch("https://api.example.com/data");34// Cache indefinitely (like static generation)5const data = await fetch("https://api.example.com/data", {6cache: "force-cache",7});89// Revalidate every 60 seconds10const data = await fetch("https://api.example.com/data", {11next: { revalidate: 60 },12});1314// Revalidate based on tags (for on-demand revalidation)15const data = await fetch("https://api.example.com/posts", {16next: { tags: ["posts"] },17});Streaming with Suspense
Don't want the entire page to wait for slow data? Use Suspense to stream parts independently:
1import { Suspense } from "react";23export default function DashboardPage() {4return (5 <div>6 <h1>Dashboard</h1>7 8 {/* This loads instantly */}9 <WelcomeMessage />10 11 {/* This streams in when ready */}12 <Suspense fallback={<p>Loading stats...</p>}>13 <SlowStats />14 </Suspense>15 16 {/* This streams independently */}17 <Suspense fallback={<p>Loading feed...</p>}>18 <ActivityFeed />19 </Suspense>20 </div>21);22}2324async function SlowStats() {25const stats = await fetch("https://api.example.com/stats");26// ... render stats27}Streaming is a superpower
⢠Users see content faster
⢠Slow APIs don't block the entire page
⢠Each section loads independently
Use this everywhere.
The 'use cache' Directive (Next.js 16)
Next.js 16 introduces the 'use cache' directive. Instead of configuring caching per-fetch, you can mark entire functions or components as cacheable:
1// Cache an entire async function2async function getProducts() {3"use cache";4const res = await fetch("https://api.example.com/products");5return res.json();6}78// Cache a component9async function ProductList() {10"use cache";11const products = await getProducts();12return (13 <ul>14 {products.map((p) => <li key={p.id}>{p.name}</li>)}15 </ul>16);17}use cache vs fetch options
⢠Simpler than per-fetch config
⢠Pair with cacheLife() for expiration control
⢠Pair with cacheTag() for on-demand revalidation