Next.js Essentials
App Router, Server Components, SSR vs SSG, data fetching, API routes
Next.js is the leading React meta-framework. The App Router (Next.js 13+) introduces React Server Components, enabling server-side rendering without client-side hydration for data-fetching components. Understanding Server vs Client components, streaming, and caching is essential for modern Next.js development.
Key Points
- App Router: file-system routing in app/ — page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx conventions
- Server Components (default): render on server, zero client JS — cannot use useState, useEffect, browser APIs, event handlers
- Client Components: "use client" directive — have interactivity, hooks, event handlers; hydrated in the browser
- Data fetching in Server Components: async function component + await fetch() — fetch is extended with Next.js caching
- Caching: fetch() in Server Components is cached by default — revalidate with next: { revalidate: 60 } or force-dynamic
- Streaming: <Suspense> in Server Components enables HTML streaming — page shell renders immediately, data arrives progressively
- Route handlers: app/api/route.ts — GET, POST, etc. — replaces pages/api; edge runtime support
- Server Actions: async functions marked "use server" — called from client without API routes; forms with server mutation
- Image, Font, Link: Next.js optimised components — automatic WebP, font subsetting, prefetching
| Feature | Server Component | Client Component |
|---|---|---|
| useState / useEffect | No | Yes |
| Event handlers (onClick) | No | Yes |
| fetch / DB / secrets | Yes | No (exposes to browser) |
| Sent to client as JS | No (HTML only) | Yes (hydrated) |
| Access to browser APIs | No | Yes |
| Default in App Router | Yes | Requires "use client" |
Next.js App Router: Server Components + Suspense streaming, Client Components, Server Actions, ISR with revalidate
// app/users/page.tsx — Server Component (no "use client")
async function UsersPage() {
// Direct DB query or fetch — runs on server only
const users = await db.user.findMany({ orderBy: { name: 'asc' } });
return (
<main>
<h1>Users</h1>
<Suspense fallback={<UserListSkeleton />}>
{/* Streaming: UserStats fetches independently */}
<UserStats />
</Suspense>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
<DeleteButton userId={user.id} /> {/* Client Component for interactivity */}
</li>
))}
</ul>
</main>
);
}
// app/users/DeleteButton.tsx — Client Component
'use client';
import { useTransition } from 'react';
import { deleteUser } from './actions'; // Server Action
export function DeleteButton({ userId }: { userId: string }) {
const [isPending, startTransition] = useTransition();
return (
<button
disabled={isPending}
onClick={() => startTransition(() => deleteUser(userId))}
>
{isPending ? 'Deleting...' : 'Delete'}
</button>
);
}
// app/users/actions.ts — Server Action
'use server';
import { revalidatePath } from 'next/cache';
export async function deleteUser(id: string) {
await db.user.delete({ where: { id } });
revalidatePath('/users'); // invalidate cache, triggers re-render
}
// app/users/[id]/page.tsx — dynamic route with caching
async function UserPage({ params }: { params: { id: string } }) {
const user = await fetch(`https://api.example.com/users/${params.id}`, {
next: { revalidate: 3600 } // ISR: cached for 1 hour, revalidated in background
}).then(r => r.json());
return <UserProfile user={user} />;
}
// Generate static params for SSG
export async function generateStaticParams() {
const users = await db.user.findMany({ select: { id: true } });
return users.map(u => ({ id: u.id }));
}Real-World Example
Server Components eliminate the client-server waterfall: instead of render → JS loads → fetch → re-render, the server renders HTML with data already in it. A typical product page that previously needed 3 client fetches (product, reviews, recommendations) can now be a Server Component with 3 parallel awaits — delivered as a single HTML response with no client JS for the fetching logic. The bundle size reduction is significant for performance on slow networks.