Next.js · cheat sheet

Next.js

App Router files, Server vs Client Components, static vs dynamic rendering, the four caches, Server Actions and middleware in Next.js 15.

The Next.js 15 App Router on one page, with the Pages Router equivalents interviewers still ask about. Checked against a Next.js 15.5 build with React 19.

App Router file conventions

File Role Remember
page.tsx UI for the URL without it a folder is not routable
layout.tsx wraps children, persists across navigations root layout must render html and body
template.tsx like layout, but remounts per navigation for enter animations, per-page effects
loading.tsx Suspense fallback for the page shown instantly; prefetched with Link
error.tsx error boundary must be 'use client'; does not catch its own segment’s layout
global-error.tsx catches root layout errors renders its own html and body
not-found.tsx UI for notFound() and unmatched URLs
route.ts HTTP endpoint (GET, POST, …) cannot share a segment with page.tsx
default.tsx fallback for a parallel slot add one per @slot
middleware.ts runs before routing, project root proxy.ts in Next.js 16

Nesting order inside a segment: layout > template > error > loading > not-found > page.

Folder URL effect
[slug] dynamic segment, params.slug
[...slug] catch-all, slug is an array, needs at least one segment
[[...slug]] optional catch-all, also matches the parent
(group) not in the URL; separate layouts
_private excluded from routing
@slot parallel route, passed to the layout as a prop
(.)x, (..)x, (...)x intercepting route (modal pattern), soft navigation only

Server vs Client Components

Server Component (default) Client Component ('use client')
Runs server only (build or request) server pre-render, then browser
async / await data yes no; use use() or a data library
State, effects, handlers no yes
Secrets, DB clients yes (import 'server-only') never
JS shipped none its module and everything it imports
  • 'use client' marks a module boundary: everything that file imports becomes client code.
  • Client code cannot import a Server Component, but can render one passed as children.
  • Props crossing the boundary must be serializable. Functions fail (“Event handlers cannot be passed to Client Component props”) unless they are Server Actions.
  • next/dynamic with ssr: false must be called inside a Client Component in Next.js 15.

Static vs dynamic rendering

Build symbol Meaning
○ static, prerendered at build
● SSG via generateStaticParams
ƒ dynamic, rendered per request
Revalidate column ISR window, e.g. 1m

A route becomes dynamic when it uses cookies(), headers(), draftMode(), connection(), searchParams, a fetch with cache: 'no-store', or dynamic = 'force-dynamic'. A plain fetch does not: the route is still prerendered and the result frozen until revalidation.

export const dynamic = 'auto';      // 'force-dynamic' | 'force-static' | 'error'
export const revalidate = 60;       // ISR window in seconds; 0 = dynamic
export const dynamicParams = true;  // false: unknown [slug] values 404
TSX
Strategy App Router Pages Router
SSG default for routes with no request data getStaticProps
Dynamic paths generateStaticParams getStaticPaths + fallback
ISR revalidate, revalidatePath/Tag revalidate in getStaticProps, res.revalidate()
SSR request APIs, no-store getServerSideProps

Next.js 15 changes

  • params, searchParams, cookies(), headers(), draftMode() are Promises: const { slug } = await params. Sync access still works but warns in dev. Codemod: npx @next/codemod@canary next-async-request-api .
  • fetch is not cached by default; GET Route Handlers are dynamic by default.
  • Router Cache staleTimes.dynamic defaults to 0; static pages stay 300 s.
  • React 19: useActionState replaces useFormState. next/form, after() and connection() from next/server.
  • Turbopack for next dev --turbopack; next.config.ts is supported.

The four caches

Cache Where Lifetime Invalidate
Request memoization server one render automatic
Data Cache server across requests and deploys revalidate, revalidateTag, revalidatePath
Full Route Cache server until revalidated or redeployed same, or a new build
Router Cache browser memory session; dynamic pages 0 s in 15 router.refresh(), actions that revalidate or set cookies
await fetch(url, { cache: 'force-cache' });            // cache indefinitely
await fetch(url, { next: { revalidate: 300 } });        // ISR for this fetch
await fetch(url, { next: { tags: ['posts'] } });        // tag for revalidateTag
export const getUser = cache(async (id: string) => db.user.find(id)); // React cache(): per-request dedupe
TSX
  • Identical GET fetches in one render run once, even with no-store.
  • router.refresh() refetches the current route but does not purge the server Data Cache.
  • unstable_cache caches non-fetch work across requests; 'use cache' is experimental in 15 and became Cache Components in 16.

Server Actions

'use server';
export async function createPost(prev: State, formData: FormData) {
  const session = await auth();                 // authorise inside every action
  if (!session) return { error: 'Sign in' };
  const parsed = schema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { error: 'Invalid input' };
  await db.post.create({ data: parsed.data });
  revalidatePath('/posts');
  redirect('/posts');                           // outside try/catch: it throws NEXT_REDIRECT
}
TSX
  • An action is a public POST endpoint identified by an action ID. Anyone can call it with curl.
  • Forms work before hydration (progressive enhancement). useActionState returns state and isPending; useFormStatus reads pending state inside the form.
  • Next checks Origin against the host (serverActions.allowedOrigins behind proxies), encrypts closure values, and drops unused actions. Default body limit 1 MB (serverActions.bodySizeLimit).
  • Use a Route Handler instead for webhooks, other clients, custom status codes or cacheable GETs.

Middleware

// middleware.ts
import { NextResponse, type NextRequest } from 'next/server';
export function middleware(req: NextRequest) {
  if (!req.cookies.has('session')) return NextResponse.redirect(new URL('/login', req.url)); // 307
  return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] }; // matches /dashboard too, not /dashboards
TypeScript
  • Runs before routing and caches on every matched request, including prefetches. Edge runtime by default; runtime: 'nodejs' is stable from 15.5.
  • Optimistic checks only. Authorise again next to the data: CVE-2025-29927 bypassed middleware with an x-middleware-subrequest header (fixed in 15.2.3).

Streaming, errors and hydration

  • loading.tsx or Suspense streams the shell first. Once streaming starts the status code is sent, so call notFound() before any Suspense boundary.
  • Production hides Server Component error messages; match the digest with server logs.
  • Static page + useSearchParams() without Suspense fails the build.
  • Hydration mismatch: React 19 error #418. Causes: Date.now(), Math.random(), locale dates, typeof window in render, localStorage, invalid nesting (div in p), browser extensions. Fix with useEffect, fixed time zones, or suppressHydrationWarning on one text node.
  • Variables are server-only unless prefixed NEXT_PUBLIC_, which is inlined at build time: rebuild to change it, and never put secrets there.
  • Load order: real env > .env.$NODE_ENV.local > .env.local (skipped in test) > .env.$NODE_ENV > .env.
  • next/image: srcset, lazy by default, priority for the LCP image, width/height or fill + sizes, images.remotePatterns for remote hosts.
  • next/link: real a element, viewport prefetch in production only, dynamic routes prefetch down to loading.tsx.
  • Metadata: export const metadata or generateMetadata in Server Components; title.template, metadataBase, sitemap.ts, robots.ts, opengraph-image.tsx.

Deployment

  • next start supports everything; output: 'standalone' for Docker images.
  • Several instances: shared cacheHandler for ISR and the Data Cache, the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEY and build ID everywhere.
  • output: 'export': static files only. No Server Actions, middleware, ISR, cookies or default image optimisation.

Practise the questions on the Next.js chapter.

esc