Fetching Data in Next.js: Server Components, Client Components, and Caching
The mental model that actually explains Next.js data fetching — where to fetch, when to cache, and how streaming works.
Next.js 13+ shipped the App Router as the new default, but the older Pages Router is still fully supported and still shows up in tutorials, older codebases, and job descriptions. Knowing the difference matters, especially if you're picking up an existing project.
Pages Router: every file in pages/ is a route, and data fetching happens via exported functions like getServerSideProps. App Router: every folder in app/ is a route (via a page.tsx file inside it), and components are React Server Components by default — they can fetch data directly inside the component with plain async/await, no special export needed.
pages/blog/[slug].tsx → /blog/my-post (Pages Router)
app/blog/[slug]/page.tsx → /blog/my-post (App Router)
The App Router also gives you colocated layout.tsx, loading.tsx, and error.tsx files per route segment — Pages Router needed a single global _app.tsx and custom logic for the same thing.
// Pages Router
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
return { props: { posts: await res.json() } };
}
// App Router — this IS the component
export default async function Page() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return <PostList posts={posts} />;
}
No special function name, no prop-drilling from a data-fetching wrapper — just await directly in the component.
Not urgently. Next.js supports both routers in the same project simultaneously, so a large existing app can migrate route-by-route rather than in one risky rewrite. For a brand-new project in 2026, start with the App Router — it's where new features land first.
Anything interactive — a button with onClick, a form with local state — needs the 'use client' directive at the top of the file. Server Components can't use hooks like useState at all; that's the trade-off for the performance win of rendering on the server by default.
The mental model that actually explains Next.js data fetching — where to fetch, when to cache, and how streaming works.
A real architecture for splitting a Laravel JSON API from a Next.js frontend — CORS, auth across domains, and on-demand revalidation.