Adios
BlogNext.js

Next.js

Next.js 16 Cheat Sheet: 75 App Router Patterns for Production

A practical Next.js 16 cheat sheet with 75 commands, file conventions, Server Component patterns, cache APIs, SEO fields, security checks, and deploy tips.

Adios teamUpdated July 17, 202622 min read

Use this as a build-side reference for Next.js 16 App Router projects. Each entry answers one narrow question, with copyable code where syntax matters.

Project setup and commands

Start, inspect, and upgrade a Next.js 16 project with repeatable commands. The examples use npm, but the framework concepts are independent of the package manager.

  1. 1

    Create a new App Router project

    create-next-app configures Next.js, React, TypeScript, and the selected tooling. Commit package-lock.json with the project.

    npx create-next-app@latest my-app --ts --app --src-dir
  2. 2

    Use the current Node.js baseline

    Next.js 16 requires Node.js 20.9 or newer. Pin a supported version in local development and CI so builds do not depend on a runner's changing default.

  3. 3

    Know the four normal scripts

    next dev starts development, next build creates the production output, next start serves that output, and your configured linter runs separately.

    "scripts": {
      "dev": "next dev",
      "build": "next build",
      "start": "next start",
      "lint": "eslint ."
    }
  4. 4

    Install reproducibly in CI

    Use npm ci when package-lock.json is committed. It rejects lockfile drift and installs the resolved dependency tree without rewriting the lockfile.

    npm ci && npm run build
  5. 5

    Use Turbopack by default

    In Next.js 16, both next dev and next build use Turbopack by default. Remove old --turbo flags unless a script needs to support another version.

  6. 6

    Temporarily keep a webpack build

    A project with custom webpack configuration can explicitly opt out while that configuration is migrated. Test both output and performance before switching the production builder.

    next build --webpack
  7. 7

    Keep application code under src

    src/app and src/lib separate application code from root configuration. The public directory and files such as next.config.ts remain at the project root.

  8. 8

    Use an import alias

    A stable alias avoids long relative paths when code moves between route folders. Keep the alias in tsconfig.json or jsconfig.json.

    import { getUser } from "@/lib/data";

Pages, layouts, and routing

The App Router is a file-system router. These conventions control URLs, shared UI, dynamic parameters, and advanced navigation behavior.

  1. 9

    Create a page

    A page.tsx file makes its folder's route publicly accessible. The default export is the UI for that URL.

    export default function Page() {
      return <h1>About</h1>;
    }
  2. 10

    Create a required root layout

    app/layout.tsx wraps every route and must render html and body. Put global CSS, language, site-wide providers, and metadata defaults here.

  3. 11

    Nest layouts by route segment

    A layout inside app/dashboard wraps the dashboard page and all descendants. Layouts persist across client navigation within their subtree.

  4. 12

    Group routes without changing the URL

    Parentheses create a route group. app/(marketing)/pricing/page.tsx still resolves to /pricing.

    app/(marketing)/pricing/page.tsx
  5. 13

    Create a dynamic segment

    Square brackets capture one URL segment. In Next.js 16, await params before reading the value.

    export default async function Page({ params }) {
      const { slug } = await params;
      return <h1>{slug}</h1>;
    }
  6. 14

    Capture several segments

    [...parts] is a required catch-all and [[...parts]] is optional. Use them for hierarchical docs or other paths whose depth is data-driven.

  7. 15

    Prebuild known dynamic paths

    generateStaticParams returns parameter objects for routes Next.js should generate at build time.

    export function generateStaticParams() {
      return posts.map((post) => ({ slug: post.slug }));
    }
  8. 16

    Reject unknown dynamic paths

    Set dynamicParams to false when only values returned by generateStaticParams should resolve. Other parameter values return 404.

    export const dynamicParams = false;
  9. 17

    Render a missing resource

    Call notFound when the route exists but its requested entity does not. Next.js renders the nearest not-found.tsx boundary.

    if (!post) notFound();
  10. 18

    Reset state with a template

    template.tsx looks like a layout but remounts on navigation. Use it when state and effects should reset; keep persistent shells in layouts.

  11. 19

    Render simultaneous route slots

    Folders such as @team and @analytics define parallel route slots passed to a parent layout. Supply default.tsx fallbacks for unmatched slots on a hard navigation.

  12. 20

    Open a route as a modal

    Intercepting routes can show another route inside the current layout during soft navigation while preserving its full-page result for refreshes and shared links.

Server and Client Components

Server Components are the App Router default. Add browser JavaScript only at components that need interaction or browser-only APIs.

  1. 21

    Keep pages server-side by default

    A Server Component can await data, use private environment variables, and render without sending its component code to the browser.

  2. 22

    Declare a Client Component

    Place the directive before imports. The client boundary includes that module and the client-side dependency graph it imports.

    "use client";
    
    import { useState } from "react";
  3. 23

    Use clients for interaction

    State, effects, event handlers, custom hooks, window, localStorage, and other browser APIs belong in Client Components.

  4. 24

    Use servers for private work

    Database queries, service credentials, large server-only libraries, and most content rendering belong in Server Components or server-only modules.

  5. 25

    Pass serializable props to clients

    Strings, numbers, booleans, arrays, plain objects, and supported React values can cross the boundary. Shape data narrowly and do not pass full private records.

  6. 26

    Mark private modules server-only

    The side-effect import makes a client import fail at build time, which protects data-access code from accidental browser use.

    import "server-only";
  7. 27

    Move the client boundary downward

    Keep a server-rendered page and layout, then isolate a search box, menu, picker, or chart as the smallest practical Client Component.

  8. 28

    Pass server UI through client children

    A Client Component can receive a Server Component as a child or another prop. This keeps the server-rendered subtree out of the client's own module graph.

  9. 29

    Add providers as deep as possible

    Context is unavailable in Server Components. Render a focused Client Component provider around only the subtree that consumes it, not the whole document by default.

Fetching, streaming, and caching

Choose freshness intentionally. Next.js 16 Cache Components are opt-in; the cheat sheet labels APIs that require that configuration.

  1. 30

    Fetch in an async Server Component

    Call an API, ORM, or database from the component that renders the result. You do not need an internal Route Handler only to call your own server.

    export default async function Page() {
      const products = await getProducts();
      return <ProductList products={products} />;
    }
  2. 31

    Start independent work together

    Promise.all prevents an avoidable request waterfall when neither operation needs the other's result.

    const [user, projects] = await Promise.all([
      getUser(),
      getProjects(),
    ]);
  3. 32

    Stream with a route loading file

    loading.tsx wraps the segment in a Suspense boundary and provides immediate fallback UI during navigation and request-time rendering.

  4. 33

    Stream one slow region

    Place Suspense around the slow component so the rest of the page can render first. Make the fallback resemble the final dimensions.

    <Suspense fallback={<ActivitySkeleton />}>
      <RecentActivity />
    </Suspense>
  5. 34

    Memoize one render pass

    React cache can deduplicate the same server-side data function and arguments during a render. It is not a persistent application data cache.

    import { cache } from "react";
    
    export const getUser = cache(async (id) => db.user.findUnique({ where: { id } }));
  6. 35

    Enable Cache Components

    Next.js 16's use cache APIs require the cacheComponents setting. Migrate intentionally because this changes rendering and caching behavior.

    const nextConfig = { cacheComponents: true };
    export default nextConfig;
  7. 36

    Cache an async function

    With Cache Components enabled, place use cache at the top of an async function or component body. Serializable arguments become part of the cache key.

    export async function getProducts() {
      "use cache";
      return db.product.findMany();
    }
  8. 37

    Set a cache lifetime

    cacheLife accepts a named profile or custom timing values. Pick a lifetime from the content's acceptable staleness, not from convenience.

    "use cache";
    cacheLife("hours");
  9. 38

    Tag related cached work

    cacheTag gives several entries a shared invalidation label, such as products or post-42.

    "use cache";
    cacheTag("products");
  10. 39

    Expire a path

    Call revalidatePath from a Server Function or Route Handler after a change makes a page or layout stale.

    revalidatePath("/blog");
  11. 40

    Revalidate by tag

    Use revalidateTag when tagged content may use stale-while-revalidate behavior. Select the cache profile that matches the freshness requirement.

  12. 41

    Read your own write with updateTag

    Call updateTag inside a Server Action when the action's user must see fresh tagged data immediately after the mutation.

    await savePost(input);
    updateTag("posts");

Forms, mutations, and Route Handlers

Writes need validation, authorization, predictable errors, and an explicit cache update. HTTP interfaces also need normal endpoint security.

  1. 42

    Declare a Server Action

    Put use server at the top of an async function or action module. Exported Server Actions must be treated as remotely callable mutation endpoints.

    "use server";
    
    export async function createPost(formData: FormData) {
      // validate, authorize, mutate, invalidate
    }
  2. 43

    Bind an action to a form

    A form can call a Server Action without a custom client submit handler. The browser's form behavior also gives you a progressive-enhancement path.

    <form action={createPost}>...</form>
  3. 44

    Validate FormData on the server

    Treat names, IDs, files, hidden inputs, and client-side validation as untrusted. Parse into an explicit schema before writing.

  4. 45

    Return expected form errors

    Use a serializable result and useActionState for validation or business errors the user can correct. Do not throw for every expected outcome.

  5. 46

    Show pending form state

    useFormStatus reads the status of the parent form submission. Disable repeat submits and give the button an honest pending label.

  6. 47

    Create a Route Handler

    route.ts exports HTTP verb functions and uses the Web Request and Response APIs. A route.ts file cannot share the same segment as page.tsx.

    export async function GET() {
      return Response.json({ status: "ok" });
    }
  7. 48

    Read a dynamic Route Handler parameter

    Route context params are asynchronous in current Next.js. Await them before querying the resource.

    export async function GET(request, { params }) {
      const { id } = await params;
      return Response.json(await getItem(id));
    }
  8. 49

    Redirect after a mutation

    Use redirect for a temporary navigation after a successful create or update, and permanentRedirect only when the resource has a lasting new canonical URL.

    redirect("/dashboard");
  9. 50

    Keep retries safe

    Webhooks and network requests can arrive more than once. Store provider event IDs or idempotency keys before repeating payments, emails, or other side effects.

SEO, images, fonts, and scripts

Next.js can generate head tags and crawler files from route code. The visible page still needs specific, useful content and semantic structure.

  1. 58

    Set static metadata

    Export metadata from a Server Component layout or page when the values do not depend on route data.

    export const metadata = {
      title: "Pricing",
      description: "Simple plans for growing teams.",
    };
  2. 59

    Generate dynamic metadata

    Use generateMetadata for entity-specific titles, descriptions, canonical URLs, and social images. Reuse the route's data access function where possible.

  3. 60

    Set metadataBase once

    A root metadataBase lets canonical links and image fields use relative paths while Next.js resolves absolute URLs.

    metadataBase: new URL("https://example.com")
  4. 61

    Generate a sitemap

    app/sitemap.ts returns public URLs and optional lastModified, changeFrequency, and priority fields. Build it from the real content catalog; Google ignores changeFrequency and priority, so keep lastModified accurate instead of manufacturing freshness.

  5. 62

    Publish robots rules

    app/robots.ts returns crawl rules and the sitemap location. Robots rules are crawl hints, not access control for private routes.

  6. 63

    Use optimized images

    next/image needs intrinsic dimensions or a fill container. Supply accurate alt text and responsive sizes; use priority only for genuinely critical above-the-fold images.

  7. 64

    Load fonts with next/font

    next/font self-hosts the selected font files and reduces external requests. Limit weights and subsets to the styles the interface actually uses.

  8. 65

    Schedule third-party scripts

    next/script controls when external JavaScript loads. Choose afterInteractive or lazyOnload unless the integration truly belongs on the critical path.

Security and configuration

Framework boundaries reduce accidental exposure only when application code keeps authorization and secret handling explicit.

  1. 66

    Keep secrets server-side

    Environment values are server-only unless their name starts with NEXT_PUBLIC_. Anything public-prefixed should be treated as browser-visible and fixed according to the build environment.

  2. 67

    Authorize every server entry point

    Check the current identity and its permission in Server Actions, Route Handlers, and protected data access. A hidden button or Proxy redirect is not a security boundary.

  3. 68

    Use Proxy for request-bound routing logic

    Next.js 16 uses proxy.ts for rewrites, redirects, and optimistic checks before a request reaches a route. Keep secure authorization near the data too.

  4. 69

    Use secure session cookies

    Set HTTP-only, secure, same-site, path, and expiry attributes to match the session design. Rotate and invalidate sessions through the authentication system.

  5. 70

    Send narrow data to the browser

    Convert records into DTOs that contain only fields the interface needs. Taint APIs can add defense in depth, but do not replace careful output shaping.

Production, debugging, and deployment

The last five checks turn framework code into an operable website with repeatable builds and observable releases.

  1. 71

    Run checks separately

    Next.js 16 does not rely on next build to run linting. Make lint, TypeScript, tests, and the production build separate CI steps.

    npm run lint
    npx tsc --noEmit
    npm test
    npm run build
  2. 72

    Test the production server

    Run next build and next start locally or in a preview. Development behavior can hide production-only import, cache, environment, and rendering problems.

  3. 73

    Read the route build summary

    The next build output identifies prerendered and request-rendered routes. Investigate a route that becomes dynamic or grows unexpectedly instead of treating the build as a pass/fail box.

  4. 74

    Expose a truthful health route

    Return a small successful response only when the process is ready for traffic. Keep secrets and detailed dependency information out of the public body.

    export function GET() {
      return Response.json({ status: "ok" });
    }
  5. 75

    Deploy the normal production process

    A standard server deployment installs from the lockfile, runs next build, starts with next start, injects runtime secrets, checks health, and promotes only a healthy release.

    build_cmd: npm ci && npm run build
    start_cmd: npm start
    
    runtime:
      name: node@24
      port: 3000
      health_path: /api/health
  All articles