principletypescriptnextjsMajor
App Router vs Pages Router: server components are the default
Viewed 0 times
Next.js 13+ with App Router
app routerpages routerserver componentsuse clientmigrationdefault server
Error Messages
Problem
Developers migrating from Pages Router to App Router expect all components to behave like React client components. In App Router, every component is a Server Component by default — no useState, no useEffect, no browser APIs.
Solution
Understand the split: Pages Router components are always client-eligible. App Router components are server-rendered by default. Mark components 'use client' only when they need interactivity or browser APIs.
// pages/index.tsx (Pages Router) — always client-eligible
import { useState } from 'react';
export default function Page() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// app/page.tsx (App Router) — Server Component by default
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return <main>{data.title}</main>;
}
// app/counter.tsx — Client Component
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// pages/index.tsx (Pages Router) — always client-eligible
import { useState } from 'react';
export default function Page() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// app/page.tsx (App Router) — Server Component by default
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return <main>{data.title}</main>;
}
// app/counter.tsx — Client Component
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Why
App Router was built around React Server Components to shift data fetching and rendering to the server, reducing client bundle size. The default-server model is a paradigm shift from the Pages Router where all components could use hooks freely.
Gotchas
- 'use client' propagates down — all imports in a client component become client-side too
- Server Components cannot use useState, useEffect, or any React hook
- Client Components can import Server Components only as children (via props), not directly
- Both routers can coexist in the same app during migration — /pages and /app simultaneously
Code Snippets
Server Component with direct data access
// app/page.tsx — Server Component, can fetch directly
export default async function Page() {
const posts = await db.post.findMany();
return <PostList posts={posts} />;
}Context
When migrating from Pages Router to App Router or starting a new Next.js 13+ project
Revisions (0)
No revisions yet.