Next.js Best Practices (App Router, v15+)
Server Components (default)
- All components are Server Components by default — no
'use client'needed - Only add
'use client'when you need: hooks, event handlers, browser APIs, or Context - Prefer server components for data fetching — no useEffect, no loading states
- Pass server data to client components as props (serializable only)
Route Conventions
page.tsx— route pagelayout.tsx— shared layout (wraps children, persists across navigations)loading.tsx— streaming fallback (instant loading UI)error.tsx— error boundary ('use client'required)not-found.tsx— 404 pageproxy.ts— request proxy/middleware (NOTmiddleware.tsin v16+)route.ts— API route handler
Data Fetching
- Fetch in server components directly (no
getServerSideProps) - Use
fetch()with Next.js caching extensions - Deduplicate requests — React auto-deduplicates identical fetches per render
- For mutations: server actions (
'use server')
Caching Strategy
// Static (build time)
export const dynamic = 'force-static';
// Dynamic (every request)
export const dynamic = 'force-dynamic';
// ISR (revalidate every N seconds)
export const revalidate = 3600;
// On-demand revalidation
import { revalidatePath, revalidateTag } from 'next/cache';
Partial Prerendering (PPR)
- Enable in
next.config.ts:experimental: { ppr: true } - Static shell renders instantly, dynamic parts stream in
- Use
<Suspense>boundaries to mark dynamic regions - No code changes needed — just wrap dynamic content in Suspense
Metadata
// Static
export const metadata: Metadata = {
title: 'Page Title',
description: 'Page description',
};
// Dynamic
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const data = await fetchData(params.id);
return { title: data.title };
}
Server Actions
'use server';
export async function createItem(formData: FormData) {
const name = formData.get('name') as string;
// Validate, write to DB, revalidate
revalidatePath('/items');
}
- Always validate input (use Zod)
- Always revalidate affected paths after mutations
- Return errors as values, not thrown exceptions
Image Optimization
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Description"
width={1200}
height={630}
priority // above the fold
placeholder="blur"
/>
Common Mistakes to Avoid
- Don't use
'use client'on pages that don't need interactivity - Don't fetch data in client components when server components work
- Don't use
useEffectfor data fetching — use server components or server actions - Don't put
middleware.tsat the root — useproxy.tsin Next.js 16+ - Don't use
router.push()for mutations — use server actions +revalidatePath() - Don't wrap entire pages in Suspense — wrap individual dynamic sections
React 19 Conventions
- No
useCallback,useMemo, orReact.memo— React Compiler handles memoization - Use
use()instead ofuseContext()for reading context - Use ternary
{condition ? <A/> : null}instead of{condition && <A/>} refis a regular prop now — noforwardRefneeded
Skill by RevealUI Studio — the agentic business runtime.
Source: RevealUIStudio/revskills — distributed by TomeVault.