Next.js Expert
Server Components are the default; "use client" is a deliberate, leaf-level choice. Fetch on the server, cache explicitly, and keep secrets off the client.
When to Use
- Building an App Router app: routing, layouts,
loading/error UI, route groups.
- Deciding Server vs Client Components and rendering mode (static / dynamic / streaming / PPR).
- Data fetching, server actions/mutations, route handlers, and
revalidate*.
- Debugging caching, hydration mismatches, or middleware.
When NOT to Use
- Generic React hooks/component patterns →
react-expert.
- Pure styling →
tailwind-expert.
- A standalone (non-Next) API/server →
nodejs-backend-expert.
Core Principles
1. The server/client boundary is the whole game
- Everything is a Server Component unless it has
"use client". Add the directive only for state, effects, event handlers, or browser APIs — and keep it at the leaves.
"use client" makes a component and its imported tree part of the client bundle. Pass server-fetched data down as props instead of pulling data into client components.
- Never import DB clients, secrets, or server-only SDKs into a client component. Guard with
import "server-only".
2. Fetch on the server, control caching explicitly
async/await directly in Server Components; co-locate the fetch with the component that needs it (React dedups identical requests in a render).
- Caching is opt-in and explicit (especially Next 15, where fetches are uncached by default):
fetch(url, { cache: "force-cache" }), { next: { revalidate: N } } for ISR, or { cache: "no-store" } for always-fresh.
- Tag data (
next: { tags: [...] }) and invalidate precisely with revalidateTag/revalidatePath after writes.
3. Mutations via server actions
"use server" functions for forms/mutations. Validate and authorize on the server — actions are public endpoints. Revalidate affected paths/tags afterward and return typed results.
4. Rendering & UX
loading.tsx + <Suspense> to stream slow parts; error.tsx for boundaries; generateStaticParams to statically render dynamic routes.
- Use the
next/image, next/font, and <Link> primitives — they fix common perf/CLS/font issues for free.
Decision Guide
| Need |
Do |
| Read data for a page |
async Server Component fetch |
| Always-fresh data |
cache: "no-store" / dynamic |
| Rebuild every N seconds |
next: { revalidate: N } (ISR) |
| Invalidate after a write |
revalidateTag / revalidatePath |
| Interactivity (state/events) |
small "use client" leaf |
| Mutation/form submit |
server action ("use server") |
| Slow section shouldn't block page |
<Suspense> + loading.tsx |
Common Mistakes
"use client" at the top of a big tree → ships everything to the browser. Push it to leaves.
- Assuming fetches are cached (Next 15 default is no-cache) → set caching explicitly.
- Secrets/DB access in a client component → leaks to the bundle; use
server-only.
- Forgetting to revalidate after a server action → stale UI.
useEffect data fetching in a page that could be a Server Component → lose SSR, caching, and SEO.
- Hydration mismatch from
Date.now()/window/random in render → guard or move to an effect/client component.
Examples
Server Component fetch with ISR + tags
// app/posts/page.tsx
export default async function Posts() {
const posts = await fetch("https://api.example.com/posts", {
next: { revalidate: 60, tags: ["posts"] },
}).then((r) => r.json());
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}
Server action: validate, authorize, revalidate
// app/actions.ts
"use server";
import { revalidateTag } from "next/cache";
import { z } from "zod";
const Input = z.object({ title: z.string().min(1) });
export async function createPost(formData: FormData) {
const { title } = Input.parse(Object.fromEntries(formData)); // validate
const user = await requireUser(); // authorize
await db.post.create({ data: { title, authorId: user.id } });
revalidateTag("posts");
}
See Also
react-expert — patterns inside client components.
typescript-expert — typing pages, actions, and params.
api-design-expert — route-handler contracts. tailwind-expert — styling.
1---2name: nextjs-expert3description: Expert Next.js App Router: Server vs Client Components, data fetching, caching, server actions, and rendering strategy. Trigger keywords: Next.js, App Router, Server Components, RSC, use client, use server, server actions, route handler, generateStaticParams, revalidate, revalidatePath, middleware, streaming, Vercel, hydration. Use for routing, data/caching strategy, mutations, or rendering decisions in Next.js.4---56# Next.js Expert78> Server Components are the default; `"use client"` is a deliberate, leaf-level choice. Fetch on the server, cache explicitly, and keep secrets off the client.910## When to Use11- Building an App Router app: routing, layouts, `loading`/`error` UI, route groups.12- Deciding Server vs Client Components and rendering mode (static / dynamic / streaming / PPR).13- Data fetching, server actions/mutations, route handlers, and `revalidate*`.14- Debugging caching, hydration mismatches, or middleware.1516## When NOT to Use17- Generic React hooks/component patterns → `react-expert`.18- Pure styling → `tailwind-expert`.19- A standalone (non-Next) API/server → `nodejs-backend-expert`.2021## Core Principles2223### 1. The server/client boundary is the whole game24- Everything is a **Server Component** unless it has `"use client"`. Add the directive only for state, effects, event handlers, or browser APIs — and keep it at the **leaves**.25- `"use client"` makes a component and its imported tree part of the client bundle. Pass server-fetched data down as props instead of pulling data into client components.26- **Never** import DB clients, secrets, or server-only SDKs into a client component. Guard with `import "server-only"`.2728### 2. Fetch on the server, control caching explicitly29- `async`/`await` directly in Server Components; co-locate the fetch with the component that needs it (React dedups identical requests in a render).30- Caching is **opt-in and explicit** (especially Next 15, where fetches are uncached by default): `fetch(url, { cache: "force-cache" })`, `{ next: { revalidate: N } }` for ISR, or `{ cache: "no-store" }` for always-fresh.31- Tag data (`next: { tags: [...] }`) and invalidate precisely with `revalidateTag`/`revalidatePath` after writes.3233### 3. Mutations via server actions34- `"use server"` functions for forms/mutations. **Validate and authorize on the server** — actions are public endpoints. Revalidate affected paths/tags afterward and return typed results.3536### 4. Rendering & UX37- `loading.tsx` + `<Suspense>` to stream slow parts; `error.tsx` for boundaries; `generateStaticParams` to statically render dynamic routes.38- Use the `next/image`, `next/font`, and `<Link>` primitives — they fix common perf/CLS/font issues for free.3940## Decision Guide41| Need | Do |42|------|-----|43| Read data for a page | `async` Server Component fetch |44| Always-fresh data | `cache: "no-store"` / `dynamic` |45| Rebuild every N seconds | `next: { revalidate: N }` (ISR) |46| Invalidate after a write | `revalidateTag` / `revalidatePath` |47| Interactivity (state/events) | small `"use client"` leaf |48| Mutation/form submit | server action (`"use server"`) |49| Slow section shouldn't block page | `<Suspense>` + `loading.tsx` |5051## Common Mistakes52- **`"use client"` at the top of a big tree** → ships everything to the browser. Push it to leaves.53- **Assuming fetches are cached** (Next 15 default is no-cache) → set caching explicitly.54- **Secrets/DB access in a client component** → leaks to the bundle; use `server-only`.55- **Forgetting to revalidate after a server action** → stale UI.56- **`useEffect` data fetching in a page that could be a Server Component** → lose SSR, caching, and SEO.57- **Hydration mismatch** from `Date.now()`/`window`/random in render → guard or move to an effect/client component.5859## Examples6061**Server Component fetch with ISR + tags**62```tsx63// app/posts/page.tsx64export default async function Posts() {65 const posts = await fetch("https://api.example.com/posts", {66 next: { revalidate: 60, tags: ["posts"] },67 }).then((r) => r.json());68 return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;69}70```7172**Server action: validate, authorize, revalidate**73```tsx74// app/actions.ts75"use server";76import { revalidateTag } from "next/cache";77import { z } from "zod";7879const Input = z.object({ title: z.string().min(1) });8081export async function createPost(formData: FormData) {82 const { title } = Input.parse(Object.fromEntries(formData)); // validate83 const user = await requireUser(); // authorize84 await db.post.create({ data: { title, authorId: user.id } });85 revalidateTag("posts");86}87```8889## See Also90- `react-expert` — patterns inside client components.91- `typescript-expert` — typing pages, actions, and params.92- `api-design-expert` — route-handler contracts. `tailwind-expert` — styling.