Next.jsCheatsheet

Next.js App Router Cheatsheet📋

The App Router file conventions, caching behavior, and server action patterns you actually need day-to-day. No fluff. Covers Next.js 15 defaults (fetch is uncached, params are async, etc.). Keep this tab open.

Quick Reference

NameSyntaxDescription
page.tsxexport default function Page({ params, searchParams }) {}Unique UI for a route. Makes the segment publicly accessible. Server Component by default.
layout.tsxexport default function Layout({ children }) {}Shared UI that wraps child segments. Persists across navigations — state is preserved.
loading.tsxexport default function Loading() {}Instant loading UI via React Suspense. Shown while the page segment streams in.
error.tsx'use client'; export default function Error({ error, reset }) {}Error boundary for a segment. Must be a Client Component. Call reset() to retry.
not-found.tsxexport default function NotFound() {}Rendered when notFound() is called in the segment. Scoped 404 page.
route.tsxexport async function GET(request: Request) {}API route handler. Export named functions for HTTP methods (GET, POST, PUT, DELETE).
template.tsxexport default function Template({ children }) {}Like layout, but re-mounts on every navigation. Useful for enter/exit animations.
default.tsxexport default function Default() {}Fallback for parallel route slots when the slot URL doesn't match after a soft navigation.
middleware.tsexport function middleware(request: NextRequest) {}Runs before every matched request. Redirect, rewrite, or set headers. Lives at project root.
[slug] / [...slug]app/blog/[slug]/page.tsx app/docs/[...slug]/page.tsxDynamic segments. [slug] matches one segment. [...slug] catches all remaining segments.
(group)app/(marketing)/about/page.tsxRoute group. Organizes routes without affecting the URL. Useful for separate layouts.
@slotapp/@analytics/page.tsx → layout gets { analytics } propParallel route slot. Renders multiple pages in the same layout simultaneously.
(.)interceptingapp/@modal/(.)photo/[id]/page.tsxIntercepting route. (.) same level, (..) one up. Classic pattern: modal on click, full page on refresh.
generateStaticParamsexport async function generateStaticParams() { return [{ slug: '...' }] }Pre-renders dynamic routes at build time. Replaces getStaticPaths from the Pages Router.
generateMetadataexport async function generateMetadata({ params }) { return { title: '...' } }Dynamic <head> metadata based on route params or fetched data. Async, runs on the server.

File Conventions

Component Render Order

layout → template → error → loading → not-found → page

Special files are rendered in a fixed hierarchy. Layouts wrap everything below, templates re-mount on nav, error boundaries catch throws, loading shows during streaming, and page is the leaf.

bash

Tips

  • Only page.tsx and route.tsx make a segment publicly accessible — you can colocate components, tests, and utils in the same folder
  • The root layout.tsx is mandatory and must render <html> and <body> tags
  • error.tsx cannot catch errors thrown in the layout of the same segment — use the parent's error boundary or global-error.tsx for root layout errors

Dynamic Route Segments

[param] | [...catchAll] | [[...optionalCatchAll]]

Brackets in folder names create dynamic segments. In Next.js 15, params is a Promise that must be awaited. Catch-all routes capture arbitrarily deep paths.

tsx

Tips

  • [[...slug]] (double brackets) makes the catch-all optional — it also matches the parent route (e.g., /docs with no slug)
  • params is now a Promise in Next.js 15 — always await it before reading properties
  • searchParams in page.tsx is also a Promise now — same await pattern

Route Groups and Private Folders

(groupName) for route groups | _folderName for private

Route groups let you apply different layouts to different sections without affecting URLs. Prefix folders with _ to exclude them from routing entirely.

bash

Tips

  • Each route group can have its own layout.tsx — great for auth pages vs. app pages with different chrome
  • You can have multiple root layouts by putting them inside route groups, but each must have its own <html> and <body>
  • Private folders (_components, _lib) are completely ignored by the router

Data Fetching

Server Component Fetching

async function + await fetch() in RSC

Server Components are async. Fetch data directly in the component body with async/await. No hooks, no loading state management, no client-side waterfall.

tsx

Tips

  • fetch() in Next.js 15 is NOT cached by default — add { cache: 'force-cache' } to opt in
  • Multiple fetches in the same render are deduped automatically if they have the same URL and options
  • Throw errors to bubble them up to the nearest error.tsx boundary

generateStaticParams for SSG

export async function generateStaticParams()

Pre-renders dynamic routes at build time. Return an array of param objects matching the dynamic segment name. Replaces getStaticPaths from the Pages Router.

tsx

Tips

  • Set dynamicParams = false to return 404 for any slug not in the returned array
  • generateStaticParams runs at build time in production — the fetch results are baked into HTML
  • Works with nested dynamic segments: return [{ category: 'tech', slug: 'post-1' }]

Parallel Data Fetching

Promise.all([fetchA(), fetchB()])

Sequential awaits create data fetching waterfalls. Use Promise.all to kick off independent requests simultaneously and cut total load time.

tsx

Tips

  • If the fetches depend on each other (order needs user ID), you cannot parallelize — use Suspense boundaries instead to stream independent sections
  • Consider splitting into separate Server Components wrapped in <Suspense> for granular streaming
  • Promise.allSettled is useful when you want partial data even if one request fails

Caching & Revalidation

fetch() Cache Options

fetch(url, { cache, next: { revalidate, tags } })

Next.js extends fetch with caching controls. In Next.js 15, fetch is uncached by default (breaking change from 14). Use force-cache or revalidate to opt in.

tsx

Tips

  • fetch without options = no caching in Next.js 15. This is a major change from Next.js 14 where force-cache was the default
  • revalidate: 0 means revalidate on every request — similar to cache: 'no-store'
  • Tags are strings — use descriptive names like 'posts', 'user-123' for targeted invalidation

On-Demand Revalidation

revalidatePath(path) | revalidateTag(tag)

Purge cached data immediately after a mutation or via a webhook. revalidatePath invalidates an entire route, revalidateTag invalidates all fetches sharing that tag.

tsx

Tips

  • revalidatePath('/blog') revalidates the page AND all fetch requests on that route
  • revalidateTag is more surgical — only fetches tagged with that string are purged
  • Call these inside Server Actions, Route Handlers, or anywhere server-side code runs

Route Segment Config

export const dynamic | revalidate | runtime | fetchCache

Export config constants from page.tsx or layout.tsx to control caching and rendering behavior at the segment level. Acts as a blanket override for all fetches in the segment.

tsx

Tips

  • dynamic = 'force-dynamic' is the App Router equivalent of getServerSideProps
  • dynamic = 'error' forces static rendering and throws if you accidentally use cookies(), headers(), or other dynamic APIs
  • These segment configs apply to all child segments unless overridden

Server Actions

Defining Server Actions

'use server' at file top or inline in function

Server Actions are async functions that run exclusively on the server. Mark them with 'use server' either per-file or per-function. They work as form actions with progressive enhancement.

tsx

Tips

  • Client Components can only import server actions from a 'use server' file — they cannot define inline server actions
  • Server Actions receive FormData as their argument when used with <form action={}>
  • Always revalidate cached data after mutations, or the UI will show stale content

Form Handling with useActionState

const [state, formAction, isPending] = useActionState(action, initialState)

Full-stack form pattern: validate with Zod on the server, return field errors, and display them on the client. Works without JavaScript (progressive enhancement).

tsx

Tips

  • useActionState's first argument to the action is the previous state, second is FormData — don't forget the signature
  • The form works even before JS loads because it's a standard HTML form with an action
  • Always validate on the server even if you validate on the client — never trust client input

Calling Server Actions from Event Handlers

onClick={() => serverAction(data)} outside of forms

Server Actions aren't limited to forms. Call them from click handlers, effects, or any client-side code. Wrap in startTransition for non-blocking updates.

tsx

Tips

  • Wrap server action calls in startTransition so React can show pending UI without blocking the main thread
  • You can pass any serializable arguments — not just FormData — when calling outside a form
  • Combine with useOptimistic for instant UI feedback while the server action runs

Metadata & SEO

Static and Dynamic Metadata

export const metadata = {} | export async function generateMetadata()

Export metadata (static) or generateMetadata (dynamic) from page.tsx or layout.tsx. Next.js automatically generates the correct <head> tags. Metadata merges down the layout tree.

tsx

Tips

  • Child metadata merges with and overrides parent metadata — set defaults in layout.tsx and override in page.tsx
  • generateMetadata can fetch data — and it's deduped with the same fetch in the page component
  • Use the metadataBase property in the root layout to set the base URL for all relative OG image paths

File-based Metadata Conventions

opengraph-image.tsx | sitemap.ts | robots.ts

Drop specially-named files into route folders for automatic OG images, sitemaps, and robots.txt. Next.js picks them up by convention and serves them at the right paths.

tsx

Tips

  • opengraph-image.tsx can be placed in any route segment — it generates the OG image for that specific route
  • sitemap.ts can also be named sitemap.xml for a static file, but the .ts version lets you generate it dynamically
  • favicon.ico, icon.png, and apple-icon.png in the app/ root are automatically served as favicons

Routing Patterns

Protected Routes with Middleware

middleware.ts + matcher config

Middleware runs before every matched request. Use it for coarse auth checks (redirecting unauthenticated users). Always verify authorization again in Server Components close to the data.

tsx

Tips

  • Middleware runs on the Edge — you cannot use Node.js APIs like fs or heavy ORMs here
  • Use the matcher array to limit which routes trigger the middleware — avoids running on static assets
  • Middleware is a first line of defense, not the only one — always check auth again in your Server Components and Server Actions

ISR (Incremental Static Regeneration)

generateStaticParams + revalidate + revalidateTag

Build static pages at deploy time, serve them from cache, and refresh individual pages on a timer or on demand. Best of both worlds: static speed with dynamic freshness.

tsx

Tips

  • Combine time-based (revalidate = 86400) and on-demand (revalidateTag) for maximum flexibility
  • Pages not in generateStaticParams are rendered on first request and cached (when dynamicParams is true)
  • ISR works out of the box on Vercel. For self-hosting, you need a shared cache store

Modal with Intercepting + Parallel Routes

@slot + (.)intercept pattern

The classic Instagram/Twitter pattern: clicking a photo opens a modal (intercepted route), refreshing the page shows the full photo page (direct route). Requires a parallel route slot + intercepting route.

tsx

Tips

  • Always add a default.tsx to the @slot folder — without it, Next.js throws a 404 on hard navigation
  • The (.) prefix means "intercept at the same level". Use (..) to go up one level
  • The modal and the full page share the same [id] param — keep data fetching in a shared function

Common Patterns

Protected Server Component with redirect

tsx

Check auth directly in the Server Component. redirect() throws internally (never returns), so code below it only runs for authenticated users. This is server-side only — no client bundle impact.

Full-stack form: Server Action + Zod + useActionState

tsx

The production-ready pattern: Zod validates on the server, field errors flow back to the form via useActionState, and the cache is refreshed after the write. Works without JS (progressive enhancement).

Watch Out For

fetch() is uncached by default in Next.js 15 — your pages may be slower than expected if you're upgrading from 14

Next.js 14 cached fetch by default (force-cache). In 15, fetch is uncached unless you explicitly opt in with { cache: 'force-cache' } or { next: { revalidate: N } }. Audit your fetches after upgrading — add caching where the data doesn't change often.

'use client' doesn't mean "client only" — it marks the boundary where Server Components stop and Client Components begin

A 'use client' component is still pre-rendered on the server (SSR), then hydrated on the client. It just can't do server-only things (direct DB access, fs, etc.). You CAN pass Server Components as children of Client Components — they stay on the server.

params and searchParams are now Promises in Next.js 15 — forgetting to await them is a runtime error

In page.tsx and layout.tsx, params and searchParams are async. Write: const { slug } = await params. The old synchronous access (params.slug) will throw. This also applies to generateMetadata and generateStaticParams context.

error.tsx cannot catch errors thrown in the layout of the same segment — it only catches errors in page.tsx and children

error.tsx wraps the page, not the layout. If your layout throws, the error bubbles up to the parent segment's error.tsx. For root layout errors, use app/global-error.tsx (which must define its own <html> and <body>).

Middleware (proxy.ts in v16) runs on the Edge runtime — you cannot use Node.js APIs like fs, Prisma, or heavy libraries

Keep middleware thin: check cookies, redirect, set headers. Move heavy logic (DB queries, auth token verification with libraries) into Server Components or Route Handlers that run on the Node.js runtime. Use a lightweight JWT decode for token checks in middleware.

Master Next.js with Stanza

Go beyond the cheatsheet with hands-on lessons and challenges.

Dive Deeper