Getting Started

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Next.js 16.3.8 (the latest release on 2026-10-03, when the facts and examples below were checked; the official documentation reports @doc-version 16.3.8), React 19.3.0, Node.js 20.9.0 or later and TypeScript 5.1 or later. It covers the App Router, which is what create-next-app generates, and it shows the Pages Router equivalent wherever the setup differs. Caching is only touched where it changes the first pages you write: the examples use the previous model (the default, in which nothing is cached unless you ask), and one section shows the same page under the opt-in Cache Components model (cacheComponents: true). Every example was built and run with Next.js 16.3.8, Node.js 24.16 and npm 11.13 on macOS, in a scratch copy of the Bookshelf — the catalogue of books, authors and reviews that the rest of this section grows, and that the Django and NestJS sections also build as a back end — and each one is followed by the official page it derives from.

The page starts at an empty folder and ends with a running, type-checked, linted and production-built application, a working dev loop, and a map of the official getting-started sequence. It does not re-teach React; if components, props and state are new, read React Getting Started first, and Server Rendering, SSG and React Server Components for the ideas behind the rendering modes that Next.js automates.

System Requirements

Next.js is a Node.js program: the dev server, the build and next start all run on it. The requirements below are the ones on the official installation page for 16.3.8.

Table 1. System requirements for Next.js 16.3
Requirement Value Notes

Node.js

20.9.0 or later

This is also the engines range of the next package. Node.js 18 stopped being supported in Next.js 16.

Operating systems

macOS, Windows (including WSL) and Linux

Turbopack ships native bindings for macOS (x64, ARM64), Windows (x64, ARM64) and Linux glibc and musl (x64, ARM64). Elsewhere (FreeBSD, OpenBSD) Next.js falls back to WebAssembly bindings, which do not support Turbopack, and you must pass --webpack.

Browsers

Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+

Modern browsers need no configuration. Polyfills and browser targets are described in the official browser-support page.

TypeScript

5.1.0 or later (optional)

Next.js 16.3 can also use TypeScript 7 for the type check in next build. The scaffold pins typescript to ^5.

React

react and react-dom 19

The App Router bundles its own React canary, which contains the stable React 19 features plus newer ones; you still declare react and react-dom in package.json. The Pages Router uses the version in your package.json, and React 18 still works there.

Package manager

npm, pnpm, Yarn or Bun

create-next-app accepts --use-npm, --use-pnpm, --use-yarn and --use-bun.

Check the Node.js version before anything else; most "it does not start" reports in a new project are an old Node.js.

Checking the toolchain
node --version        # must be v20.9.0 or later
npm --version
npx next --version    # inside a project: prints the installed Next.js

Creating a Project with create-next-app

The quickest start is create-next-app, which writes the folder layout, installs the dependencies and configures TypeScript, the linter, the CSS toolchain, the import alias and the agent guidance file. --yes skips every prompt and uses your saved preferences or the defaults.

Quick start: the recommended defaults without prompts
npx create-next-app@latest bookshelf --yes
cd bookshelf
npm run dev

Run without arguments, the CLI asks interactively. The first prompt names the project; the second chooses between the recommended defaults, your previous settings, or a customised set. The prompts below are the ones documented for Next.js 16.3.8.

The prompts of create-next-app
What is your project named? my-app
Would you like to use the recommended Next.js defaults?
    Yes, use recommended defaults - TypeScript, ESLint, Tailwind CSS, App Router, AGENTS.md
    No, reuse previous settings
    No, customize settings - Choose your own preferences

# If you choose "customize settings":
Would you like to use TypeScript? No / Yes
Which linter would you like to use? ESLint / Biome / None
Would you like to use React Compiler? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the import alias (`@/*` by default)? No / Yes
What import alias would you like configured? @/*
Would you like to include AGENTS.md to guide coding agents to write up-to-date Next.js code? No / Yes

Every answer has a flag, so a project can be created non-interactively and reproducibly in a script or in CI.

Table 2. The create-next-app options
Option What it does Default

--ts or --typescript, --js or --javascript

TypeScript or JavaScript project.

TypeScript

--eslint, --biome, --no-linter

ESLint (with @next/eslint-plugin-next rules), Biome (linter plus formatter), or no linter. --no-eslint also appears in the CLI output as the way to opt out of ESLint.

ESLint

--tailwind, --no-tailwind

Tailwind CSS configuration. Without it you get a plain CSS module (page.module.css) in the starter page.

Tailwind CSS v4

--react-compiler

Adds babel-plugin-react-compiler and reactCompiler: true in next.config.ts.

Off

--app, --no-app

App Router, or the Pages Router (--no-app).

App Router

--src-dir

Puts application code in src/ (src/app), keeping configuration files at the root.

No src/

--import-alias <alias>

The paths alias in tsconfig.json or jsconfig.json.

@/*

--api

A headless project with only Route Handlers (no layout and no page).

Off

--empty

A minimal page and layout: no starter content and no public/ assets.

Off

--turbopack, --webpack

Written into the generated scripts. Turbopack is the default bundler.

Turbopack

--agents-md, --no-agents-md

Includes (or omits) AGENTS.md and a CLAUDE.md that references it.

Included

--use-npm, --use-pnpm, --use-yarn, --use-bun

Chooses the package manager to bootstrap with.

Detected from how it was invoked

-e, --example <name or github-url> and --example-path

Bootstraps from an official example or from any public GitHub repository.

None

--skip-install, --disable-git

Skips installing packages, or skips git init.

Install and git init

--yes, --reset-preferences

Uses saved preferences or the defaults for anything not given; or forgets the saved preferences.

Prompts

The table follows the official reference page. npx create-next-app@latest --help on 16.3.8 additionally lists --rspack (an experimental Rspack bundler option) and does not print --turbopack, --webpack, --no-linter or --no-app, so treat --help as the authority for what your installed version accepts.

What the defaults generate

The recommended defaults were run with npx create-next-app@latest bookshelf --yes --use-npm --disable-git. The tool installed the three runtime packages and eight development packages, generated the route types, and wrote this tree:

The default project (App Router, TypeScript, ESLint, Tailwind CSS v4, AGENTS.md)
bookshelf/
├── AGENTS.md              guidance for coding agents (points at the docs bundled in node_modules)
├── CLAUDE.md              one line: @AGENTS.md
├── README.md
├── app/
│   ├── favicon.ico
│   ├── globals.css        Tailwind v4 entry: @import "tailwindcss"
│   ├── layout.tsx         root layout (required)
│   └── page.tsx           the home page, route /
├── eslint.config.mjs      ESLint flat config
├── next-env.d.ts          generated: makes the Next.js types visible to TypeScript
├── next.config.ts         empty NextConfig
├── package.json
├── package-lock.json
├── postcss.config.mjs     @tailwindcss/postcss
├── public/                five starter SVGs, served from /
└── tsconfig.json          strict, "paths": { "@/*": ["./*"] }

The generated package.json is short. Note the exact versions: next and eslint-config-next are pinned to the version of the tool, and react is pinned to the React release the tool was published with (19.2.8 for create-next-app 16.3.8, although react 19.3.0 was already on npm; upgrade it with the command in the upgrading section below).

package.json as generated on 2026-10-03
{
  "name": "bookshelf",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "next": "16.3.8",
    "react": "19.2.8",
    "react-dom": "19.2.8"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.3.8",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

The tsconfig.json enables the Next.js TypeScript plugin and the @/* import alias, and includes the generated route types, so PageProps and LayoutProps are available without imports. The alias maps @/lib/books to lib/books at the project root (or to src/lib/books when you chose --src-dir).

tsconfig.json (excerpt) as generated
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "strict": true,
    "noEmit": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "incremental": true,
    "plugins": [{ "name": "next" }],
    "paths": { "@/*": ["./*"] }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts",
    ".next/dev/types/**/*.ts",
    "**/*.mts"
  ],
  "exclude": ["node_modules"]
}

Source: TypeScript

The ESLint setup is a flat config that extends the Next.js rule sets. Since Next.js 16 the linter is run by its own CLI (eslint), not by next lint.

eslint.config.mjs as generated
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  // Override default ignores of eslint-config-next.
  globalIgnores([
    // Default ignores of eslint-config-next:
    ".next/**",
    "out/**",
    "build/**",
    "next-env.d.ts",
  ]),
]);

export default eslintConfig;

Source: ESLint

Three details of the scaffold matter later. First, .gitignore contains .env*, so environment files are not committed by default (the official environment-variables guide warns that you almost never want to commit them). Second, next-env.d.ts is listed in .gitignore, because Next.js regenerates it. Third, the starter layout.tsx loads the Geist fonts through next/font/google; the font files are fetched when the project is built, so the first build needs network access (self-hosting the font files is covered with fonts in the official sequence listed below).

The other flavours, as generated

Each flavour below was generated with --skip-install --disable-git and its files and package versions were inspected. The default project above, which is the base of the Bookshelf in the next sections, was also built, type-checked and linted.

Table 3. What each non-default flag changes (verified on create-next-app 16.3.8)
Command Result

--ts --eslint --tailwind --src-dir --app --import-alias "@/*"

app/ moves to src/app/; tsconfig.json maps "@/" to ["./src/"]; next.config.ts, eslint.config.mjs, postcss.config.mjs and public/ stay at the root.

--biome --no-tailwind

No ESLint: @biomejs/biome (2.4.2) is a development dependency, a biome.json is written, and the scripts become "lint": "biome check" and "format": "biome format --write". Without Tailwind the starter page uses app/page.module.css.

--js --no-linter --no-tailwind --no-agents-md

app/layout.js and app/page.js, a jsconfig.json carrying the same @/* paths, and next.config.mjs typed with a JSDoc @type comment. No AGENTS.md, CLAUDE.md or linter.

--api

Only app/route.ts (GET returns {"message":"Hello world!"}) and app/[slug]/route.ts. There is no layout and no page; a back end made of Route Handlers.

--empty

A minimal app/layout.tsx and app/page.tsx ("Hello world!") with globals.css, ESLint and Tailwind, but no starter artwork in public/.

--react-compiler --yes

Adds babel-plugin-react-compiler 1.0.0 as a development dependency and reactCompiler: true in next.config.ts.

The generated --api template is a good minimal Route Handler. The second handler reads a dynamic segment, and since Next.js 15 params is a Promise that must be awaited.

The route handlers generated by --api
// app/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ message: "Hello world!" });
}
// app/[slug]/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ slug: string }> },
) {
  const { slug } = await params;
  return NextResponse.json({ message: `Hello ${slug}!` });
}

Source: route.js

An example from the official repository, or from any public GitHub repository, replaces the template altogether:

Starting from an example
npx create-next-app@latest --example [example-name] bookshelf
npx create-next-app@latest --example "https://github.com/<owner>/<repo>" bookshelf

The AGENTS.md and CLAUDE.md files

Since 16.2, create-next-app includes AGENTS.md by default (--no-agents-md opts out), together with a one-line CLAUDE.md containing @AGENTS.md. The file does not teach anything itself: it tells a coding agent to read the documentation that ships inside the next package, in node_modules/next/dist/docs/, which always matches the installed version, instead of relying on training data. A human reader gets the same benefit, because those files are the official docs as Markdown.

The block that next dev and create-next-app maintain in AGENTS.md
<!-- BEGIN:nextjs-agent-rules -->

# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.

<!-- END:nextjs-agent-rules -->

From 16.3, next dev also writes and maintains this block in existing projects when it detects an AI coding agent in the environment (it was observed in the manual-installation run below, where the log printed that it had generated AGENTS.md and CLAUDE.md). Content you write outside the BEGIN and END markers is preserved. To opt out of the automatic generation, set agentRules to false:

Opting out of the managed agent files
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  agentRules: false,
}

export default nextConfig

On 16.2 the docs are bundled but the file is not auto-generated; on 16.1 and earlier the docs are not bundled and the legacy npx @next/codemod@canary agents-md command downloads a version-matched copy into .next-docs/.

Manual Installation

Nothing in Next.js requires create-next-app: it is a convenience. A project is three packages, three scripts and two files. This sequence was run from an empty folder, with the file contents exactly as shown.

Manual installation: packages and scripts
mkdir bookshelf && cd bookshelf
npm init -y
npm i next@latest react@latest react-dom@latest
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "lint:fix": "eslint --fix"
  }
}

The app directory needs one file, the root layout, which must render the <html> and <body> tags; the home page is optional but nothing would be served at / without it.

The smallest App Router application: app/layout.tsx and app/page.tsx
// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
// app/page.tsx
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

Start npm run dev once. Because the project contains TypeScript files and no TypeScript, Next.js installs the missing packages and writes a tsconfig.json for you. The first start, as logged:

The first npm run dev of a manual installation (trimmed)
▲ Next.js 16.3.8 (Turbopack)
- Local:         http://localhost:3000
✓ Ready in 157ms
It looks like you're trying to use TypeScript but do not have the required package(s) installed.
Installing devDependencies (npm):
- typescript
- @types/react
- @types/node

  We detected TypeScript in your project and created a tsconfig.json file for you.

✓ Generated AGENTS.md and CLAUDE.md for AI agents. Set `agentRules: false` in next.config to disable.

Two things differ from the scaffolded project. The auto-installed typescript is the newest release (7.0.2 on the checked date), whereas the scaffold pins ^5; both passed next build here. And the generated tsconfig.json has "strict": false; set it to true for a new project, or you give up most of what TypeScript offers.

A real pitfall of this route: on npm 11.13, npm init -y writes "type": "commonjs" into package.json. Turbopack then refuses ES-module syntax in the .tsx files (JSX compiles to an import) and every request returns HTTP 500.

The error caused by "type": "commonjs" in package.json
⨯ ./app/layout.tsx
Error: Specified module format (CommonJs) is not matching the module format of the source code (EcmaScript Modules)
The CommonJs module format was specified in the package.json that is affecting this source file ...
 GET / 500 in 988ms

The fix is to delete the "type" line (or set it to "module"); after removing it the same files returned HTTP 200. create-next-app never writes the field.

Source: Installation: manual installation (the error output itself was observed in the scratch project)

For the Pages Router, the only difference is the folder: create pages/index.tsx instead of app/. There is no required root layout. This minimal project also builds, and the route table prints Route (pages):

Manual installation, Pages Router: pages/index.tsx
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}
Build output of the Pages Router project
Route (pages)
┌ ○ /
└ ○ /404

○  (Static)  prerendered as static content

If you forget the root layout in an App Router project, next dev creates one for you when it starts.

The optional public folder at the project root serves static files from the base URL, so public/profile.png is /profile.png. An optional src folder holds app (or pages) when you want to separate code from configuration.

Source: public and src

The Commands: dev, build and start

The next binary is the whole toolchain. Three commands make the lifecycle; the others are helpers.

Table 4. The next CLI commands in 16.3
Command Purpose

next dev

Development server with Hot Module Replacement, error reporting and on-demand compilation. Output goes to .next/dev, so next dev and next build can run at the same time.

next build

Optimised production build; prints the route table. Since 16 it no longer runs the linter, and no longer prints bundle sizes.

next start

Starts the production server. The application must have been compiled with next build first.

next info

Prints operating system, Node.js, package manager and package versions for bug reports (--verbose for more).

next typegen

Generates the route types (PageProps, LayoutProps, typed routes) and next-env.d.ts without a build, for next typegen && tsc --noEmit in CI.

next upgrade

Upgrades Next.js (and its docs) to the latest version; available from 16.1.

next telemetry

Enables or disables the anonymous telemetry (--enable, --disable).

next experimental-analyze

Turbopack bundle analyzer, labelled experimental. --output writes static files to .next/diagnostics/analyze.

Source: next CLI

Running next without a command is an alias for next dev. That has a nasty consequence for a removed command: in Next.js 16.3.8 next lint does not exist any more, and the CLI treats lint as the project directory of next dev:

A removed command is read as a directory
$ npx next lint
Invalid project directory provided, no such directory: /path/to/bookshelf/lint

Use the lint script (eslint) instead. A project that still has next lint in its scripts can be migrated with npx @next/codemod@canary next-lint-to-eslint-cli ..

next dev

Starting and requesting the development server (assembled from real runs, with the network address replaced)
$ npm run dev
> bookshelf@0.1.0 dev
> next dev

▲ Next.js 16.3.8 (Turbopack)
- Local:         http://localhost:3000
- Network:       http://192.168.1.20:3000
✓ Ready in 98ms
✓ Running next.config.ts took 13ms

 GET / 200 in 731ms (next.js: 656ms, application-code: 74ms)
 GET / 200 in 11ms (next.js: 1.5ms, application-code: 10ms)
✓ Compiled in 9ms

The first request compiles the route on demand (Turbopack bundles lazily, only what the server is asked for), later ones are served in milliseconds, and each saved edit prints Compiled in …​. The request lines split the time into framework (next.js) and your code (application-code).

Table 5. The options of next dev that you use most
Option Effect

-p, --port <port> or PORT=4000

The port (default 3000). PORT cannot be set in a .env file, because the HTTP server starts before environment files are loaded. With npm run, pass flags after --: npm run dev — -p 4000.

-H, --hostname <hostname>

The hostname to bind (default 0.0.0.0, so it is reachable from other devices on the network).

--turbopack, --webpack

Turbopack is the default; --webpack selects webpack. --turbo is an alias of --turbopack.

--inspect

Attaches the Node.js debugger to the process that runs your code (16.1). --inspect-brk and --inspect-wait must go through NODE_OPTIONS.

--experimental-https

HTTPS with a self-signed certificate generated for localhost, for webhooks and authentication callbacks. Use --experimental-https-key, --experimental-https-cert and --experimental-https-ca to bring your own.

[directory]

A project directory other than the current one.

next build

The build prints the route table. The legend is the quickest way to learn how Next.js classified each route. This is the table for the Bookshelf as built in the next section (previous model):

next build for the first Bookshelf pages (trimmed)
▲ Next.js 16.3.8 (Turbopack)

  Creating an optimized production build ...
✓ Compiled successfully in 2.3s
  Running TypeScript ...
  Finished TypeScript in 869ms ...
  Collecting page data using 7 workers ...
✓ Generating static pages using 7 workers (8/8) in 245ms
  Finalizing page optimization ...

Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /books
├   /books/[id]
│ ├ ● /books/1
│ ├ ● /books/2
│ └ ● /books/3
└ ƒ /search

○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses generateStaticParams)
ƒ  (Dynamic)  server-rendered on demand

Useful next build options: --debug (also prints rewrites, redirects and headers), --debug-prerender (readable stack traces for prerender errors, never deploy such a build), --debug-build-paths="app/page.tsx" (build one route only), --webpack, --profile (React profiling in production) and --no-mangling.

next start

Serving the production build on another port
npm run build
PORT=3000 npm start          # or: npx next start -p 3000

next start serves what next build produced, so a change to the source has no effect until you rebuild. Behind a load balancer, pass --keepAliveTimeout <ms> larger than the balancer’s idle timeout. In 16.2 and later next start --inspect attaches the Node.js debugger to the production server too.

Turbopack and webpack

Since Next.js 16 Turbopack is the default bundler for both next dev and next build. Its file-system cache is on by default (.next/dev/cache/turbopack for dev, .next/cache/turbopack for builds). A project with a custom webpack configuration fails next build until you pick a bundler explicitly. To keep webpack, change the scripts:

Opting back in to webpack
{
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build --webpack",
    "start": "next start"
  }
}

A next build --webpack of the Bookshelf was run and succeeded (the banner reads Next.js 16.3.8 (webpack)).

The life cycle in one picture

flowchart LR A["npx create-next-app"] --> B["npm run dev
writes .next/dev"] B --> C{"Happy with the app?"} C -->|"no: edit and save"| B C -->|yes| D["npm run lint
eslint"] D --> E["npm run build
next build, writes .next"] E --> F["npm start
next start"] F --> G["Browser"] B --> G
Figure 1. From scaffold to production: `dev` and `build` write to different folders

The First Page and Layout, Step by Step

The starter page is replaced by pages that belong to the Bookshelf. Everything below was written into the scaffolded project and then type-checked (tsc --noEmit), linted (eslint), built (next build) and served (next start). Routing is explained in depth later in this section; here the goal is only to see the file conventions at work.

Next.js routes by file system: folders inside app define URL segments, and special files inside them define the UI of that segment. A page.tsx makes a segment publicly reachable; a layout.tsx wraps its page and every nested segment, keeps its state and does not re-render on navigation between those children.

The root layout

app/layout.tsx: the root layout with metadata and navigation
import type { Metadata } from 'next'
import Link from 'next/link'
import './globals.css'

export const metadata: Metadata = {
  title: { default: 'Bookshelf', template: '%s | Bookshelf' },
  description: 'A small catalogue of books, authors and reviews.',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <header>
          <nav>
            <Link href="/">Home</Link> <Link href="/books">Books</Link>
          </nav>
        </header>
        <main>{children}</main>
      </body>
    </html>
  )
}

The rules of the root layout, from the official reference:

  • It is required, lives at the top of app, and must define <html> and <body>.

  • Do not write <head> tags such as <title> and <meta> by hand; export metadata (or generateMetadata) and Next.js streams and de-duplicates the head elements. The title.template above turns a page title Books into Books | Bookshelf.

  • There can be several root layouts: any layout with no layout.js above it is a root layout, for example with the route groups app/(shop)/layout.js and app/(marketing)/layout.js. Navigating between different root layouts is a full page load, not a client-side navigation.

  • The root layout may sit under a dynamic segment such as app/[lang]/layout.js (internationalisation); that segment then becomes a root parameter.

  • A layout cannot read the request object. It is cached on the client between navigations and does not re-render, so use cookies() and headers() in Server Components and Functions instead, not in the layout’s props.

A home page, a catalogue and a typed dynamic page

lib/books.ts: the data, a plain module (no database yet)
export type Book = { id: string; title: string; author: string }

export const books: Book[] = [
  { id: '1', title: 'Dune', author: 'Frank Herbert' },
  { id: '2', title: 'Solaris', author: 'Stanislaw Lem' },
  { id: '3', title: 'Neuromancer', author: 'William Gibson' },
]

export function getBook(id: string): Book | undefined {
  return books.find((book) => book.id === id)
}
app/page.tsx: the home page, route /
import Link from 'next/link'

export default function Page() {
  return (
    <>
      <h1>Bookshelf</h1>
      <p>
        Browse the <Link href="/books">catalogue</Link>.
      </p>
    </>
  )
}
app/books/page.tsx: route /books, with page-level metadata
import type { Metadata } from 'next'
import Link from 'next/link'
import { books } from '@/lib/books'

export const metadata: Metadata = { title: 'Books' }

export default function BooksPage() {
  return (
    <>
      <h1>Books</h1>
      <ul>
        {books.map((book) => (
          <li key={book.id}>
            <Link href={`/books/${book.id}`}>{book.title}</Link> by {book.author}
          </li>
        ))}
      </ul>
    </>
  )
}
app/books/[id]/page.tsx: a dynamic segment, prerendered with generateStaticParams
import { notFound } from 'next/navigation'
import { books, getBook } from '@/lib/books'

export function generateStaticParams() {
  return books.map((book) => ({ id: book.id }))
}

export default async function BookPage(props: PageProps<'/books/[id]'>) {
  const { id } = await props.params
  const book = getBook(id)
  if (!book) notFound()

  return (
    <article>
      <h1>{book.title}</h1>
      <p>by {book.author}</p>
    </article>
  )
}

Points worth noticing in these four files:

  • Link is the primary navigation component. It extends <a> with prefetching and client-side transitions, so clicking Books swaps the page without a full reload while the layout stays mounted.

  • The module path alias @/lib/books comes from the paths entry written by create-next-app; getBook and books are imported from the project root, not through ../../../.

  • PageProps<'/books/[id]'> is a globally available helper type. It is generated into .next/types (and .next/dev/types) by next dev, next build or next typegen, so no import is needed, and it types params from the route string. Static routes resolve params to an empty object.

  • params is a Promise and must be awaited. Next.js 15 made the request APIs asynchronous (with a temporary synchronous compatibility layer), and Next.js 16 removed the synchronous access. A page that reads params.id directly is a leftover of the older model.

  • generateStaticParams returns the segment values to prerender at build time, which is why the route table shows /books/1, /books/2 and /books/3 as ●. A value outside the list is rendered on demand; notFound() renders the not-found UI and the response status is 404 (verified: /books/99 returns 404 under the previous model).

  • A page that is not a Client Component is a Server Component by default: it runs on the server (at build time here, because everything it needs is known), its code never reaches the browser, and the browser receives HTML plus the RSC payload used for later navigations.

Requests against the production build (previous model)
$ curl -s -o /dev/null -w '%{http_code}\n' localhost:3000/books/2
200
$ curl -si localhost:3000/books/2 | grep -i -E '^(x-nextjs|cache-control)'
x-nextjs-cache: HIT
x-nextjs-prerender: 1
x-nextjs-stale-time: 300
Cache-Control: s-maxage=31536000
$ curl -s -o /dev/null -w '%{http_code}\n' localhost:3000/books/99
404

The prerendered page is served from the build output with a long shared-cache lifetime; this is where static generation, the subject of its own page later in the section, begins.

A nested layout

A layout inside a folder wraps only that segment and below. The root layout wraps the books layout, which wraps both /books and /books/[id].

app/books/layout.tsx: a nested layout
export default function BooksLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section aria-label="Catalogue">{children}</section>
}

Search parameters make a page dynamic

A Server Component page reads the query string through the searchParams prop, which is also a Promise. Reading it needs the incoming request, so the route stops being prerendered and is rendered on every request (the ƒ in the route table).

app/search/page.tsx: server-rendered search, previous model
import { books } from '@/lib/books'

export default async function SearchPage(props: PageProps<'/search'>) {
  const { q } = await props.searchParams
  const term = (Array.isArray(q) ? q[0] : q)?.toLowerCase() ?? ''
  const hits = books.filter((book) => book.title.toLowerCase().includes(term))

  return (
    <>
      <h1>Search</h1>
      <ul>
        {hits.map((book) => (
          <li key={book.id}>{book.title}</li>
        ))}
      </ul>
    </>
  )
}
The dynamic route is never cached by the browser or a shared cache
$ curl -s 'localhost:3000/search?q=sol' | grep -o '<li>[^<]*</li>'
<li>Solaris</li>
$ curl -si 'localhost:3000/search?q=sol' | grep -i cache-control
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate

Use the searchParams prop when the parameters decide which data to load (filtering, pagination); use the client hook useSearchParams when they only affect the client, for a list that was already loaded.

The same page under Cache Components

With cacheComponents: true the model is reversed: the build prerenders as much as it can into a static shell, and anything that needs the request must be inside a <Suspense> boundary, or the build fails. The page above, built unchanged with the option on, stops the build:

Build error with cacheComponents and an un-suspended searchParams
Error: Route "/search": Next.js encountered uncached or runtime data during prerendering.

`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`, or `connection()` accessed outside of `<Suspense>`
prevents the route from being prerendered, blocking the page load and leading to a slower user experience.

Ways to fix this:
  - [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
  - [cache] For uncached data (`fetch`, database calls): cache the access with `"use cache"` ...
  - [block] Set `export const instant = false` to allow a blocking route

Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic

The error page blocking-prerender-dynamic documents the fix: stream the dynamic part behind a fallback. Under Cache Components the route becomes partially prerendered (◐): the heading and the fallback are in the shell, the results stream in.

next.config.ts: opting in to Cache Components
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig
app/search/page.tsx: the Cache Components version
import { Suspense } from 'react'
import { books } from '@/lib/books'

async function Results({
  query,
}: {
  query: PageProps<'/search'>['searchParams']
}) {
  const { q } = await query
  const term = (Array.isArray(q) ? q[0] : q)?.toLowerCase() ?? ''
  const hits = books.filter((book) => book.title.toLowerCase().includes(term))

  return (
    <ul>
      {hits.map((book) => (
        <li key={book.id}>{book.title}</li>
      ))}
    </ul>
  )
}

export default function SearchPage(props: PageProps<'/search'>) {
  return (
    <>
      <h1>Search</h1>
      <Suspense fallback={<p>Searching...</p>}>
        <Results query={props.searchParams} />
      </Suspense>
    </>
  )
}
The route table under Cache Components
Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /books
├   /books/[id]
│ ├ ◐ /books/[id]
│ ├ ○ /books/1
│ ├ ○ /books/2
│ └ ○ /books/3
└ ◐ /search

○  (Static)             prerendered as static content
◐  (Partial Prerender)  prerendered as static HTML with dynamic server-streamed content

The extra ◐ /books/[id] row is the shell for book ids that were not listed in generateStaticParams: 16.3 can serve an app shell for unknown params and upgrade it to a full page in the background. cacheComponents and partialPrefetching are still opt-in in 16.3; the 16.3 announcement says their behaviour will become the default in a future major version, which is why this section teaches both models and labels each example.

A first Client Component

Server Components cannot hold state or handle events. Mark the smallest interactive piece with 'use client' and import it into the server page: the directive turns that file and everything it imports into client code that is hydrated in the browser, while the page around it remains server-rendered.

components/like-button.tsx: a Client Component
'use client'

import { useState } from 'react'

export function LikeButton() {
  const [likes, setLikes] = useState(0)
  return <button onClick={() => setLikes(likes + 1)}>Like ({likes})</button>
}
app/page.tsx: the home page with the Client Component
import Link from 'next/link'
import { LikeButton } from '@/components/like-button'

export default function Page() {
  return (
    <>
      <h1>Bookshelf</h1>
      <p>
        Browse the <Link href="/books">catalogue</Link>.
      </p>
      <LikeButton />
    </>
  )
}
Table 6. Where each piece of the first Bookshelf runs
File Runs What reaches the browser

app/layout.tsx, app/page.tsx, app/books/*

Server, at build time (previous model: static and generateStaticParams routes)

HTML, CSS links, and the RSC payload for client-side navigations. None of this component code is in the JavaScript bundle.

app/search/page.tsx

Server, on every request (previous model)

HTML streamed for each request; no search code in the bundle.

components/like-button.tsx

Server for the first HTML, then client for hydration and every click

Its JavaScript, react and the router runtime; the button works after hydration.

lib/books.ts

Wherever it is imported from

Nothing, while it is only imported by Server Components. If a Client Component imports it, the data ships in the bundle.

Fast Refresh

Fast Refresh is a React feature integrated into Next.js that applies your edits in the browser within about a second while keeping temporary client-side state. It has been on by default since Next.js 9.4 and needs no configuration. Its behaviour depends on what the edited file exports:

  • A file that exports only React components is re-evaluated alone and its components re-render; styles, rendering logic, handlers and effects are all editable.

  • A file that also exports non-component values (a constant, a helper) is re-run together with the files that import it.

  • A file imported by something outside the React tree makes Fast Refresh fall back to a full reload.

Error resilience is part of the design. A syntax error shows an overlay; fixing it and saving removes the overlay with no reload and with no loss of state. A runtime error inside a component shows a contextual overlay; state is kept if the error did not occur while rendering, and React remounts the application with the new code if it did. Error boundaries retry on the next edit, so one around a section can stop a render error from resetting the whole page.

The behaviour was checked in a browser against the LikeButton above on a running next dev: the button was clicked twice (Like (2)), the label in the source was changed to Favourite, and after the save the button read Favourite (2) — the new code was live and the state survived.

The edit that was used to check state preservation
-  return <button onClick={() => setLikes(likes + 1)}>Like ({likes})</button>
+  return <button onClick={() => setLikes(likes + 1)}>Favourite ({likes})</button>
flowchart TD A["Edit and save a file"] --> B["next dev recompiles
only what changed, with Turbopack"] B --> C{"What does the file export?"} C -->|"only React components"| D["Fast Refresh
re-render that module, keep state"] C -->|"components and other values"| E["Re-run the file
and the files that import it"] C -->|"imported outside the React tree"| F["Full reload"] B --> G{"Error?"} G -->|"syntax or build error"| H["Overlay in the browser"] G -->|"runtime error while rendering"| I["Overlay, then remount
after the next fix"] G -->|"no"| J["Browser shows the update in about a second"] H -->|"fix and save"| B I -->|"fix and save"| B D --> J E --> J
Figure 2. The dev loop

Source: Fast Refresh

When state is reset on every edit, one of these limitations applies, and each has a fix:

Table 7. Why Fast Refresh may reset state
Cause What to do

The file is a class component (only function components and Hooks keep state)

Convert to a function component.

The file exports something that is not a component next to the component

Move the constant or helper to its own file.

The file exports the result of a higher-order component such as HOC(Wrapped) and the result is a class

Use a function component.

An anonymous default export, such as export default () ⇒ <div />

Name the component. The name-default-component codemod does this for a large code base.

An import whose case does not match the file name (./header against ./Header)

Fix the case; both fast and full refresh fail on it.

Two tools help. Add the comment // @refresh reset anywhere in a file to make Fast Refresh remount that file’s components on every edit, which is handy for an animation that only runs on mount. And remember that hooks with dependency arrays (useEffect, useMemo, useCallback) always re-run during Fast Refresh, ignoring their dependencies; code that tolerates an occasional extra effect run is better code anyway, and React Strict Mode enforces it.

The Dev Overlay and the Next.js DevTools

In next dev a small indicator in a corner of the page shows the state of the route you are looking at, and errors open an overlay over the page. None of it exists in a production build.

Table 8. What the development UI offers
Feature What it does Since

On-screen indicator

Shows context about the current route, such as whether it is static or dynamic. position moves it; setting devIndicators to false hides it while compile and runtime errors are still shown.

15.0 (static indicator), 15.2 (position)

Error overlay

Shows build, runtime and type errors with a code frame. A Node.js icon under the version label copies the URL of the server inspector to the clipboard, for debugging server errors in browser DevTools.

Always; Error.cause chains (up to five levels) from 16.2

Hydration diff

When server and client HTML disagree, the overlay shows a diff in which each differing line is labelled as coming from the client or from the server, so the diverging text is visible.

16.2

Instant Insights

A DevTools panel that surfaces the navigations you meet that are not instant, and offers a prompt that teaches an agent how to apply the fix. It is part of the Instant Navigations work.

16.3

Navigation Inspector

A DevTools panel with a Pause on navigations toggle that freezes a page load or a client navigation at its loading shell, to see what a user would see before dynamic data streams in. Prefetching is off in development, so this is the way to preview it. Available with Cache Components.

16.3

next.config.ts: moving the indicator (this configuration was loaded and built without warnings)
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  devIndicators: {
    position: 'bottom-right', // 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
  },
}

export default nextConfig

The options appIsrStatus, buildActivity and buildActivityPosition were deprecated in 15.2 and removed in 16.0; the indicator itself remains. The indicator is the quickest check that a route you expect to be static really is: if it is marked dynamic, the route uses a request-time API (cookies(), headers(), searchParams) or an uncached data request. Confirm with next build --debug, where static routes print ○ and dynamic ones ƒ.

Debugging

Any debugger that attaches to Node.js works. The official guide gives VS Code launch configurations, browser DevTools and WebStorm. The two commands you need most are:

Debugging the server and the client
npm run dev -- --inspect                  # Node.js debugger for the server code (16.1+)
NODE_OPTIONS=--inspect-brk npm run dev    # break on the first line

Open chrome://inspect and attach to the Next.js process for server code. For client code open the browser DevTools Sources tab; the debugging guide says client sources appear under webpack://_N_E/./. A debugger; statement in client code pauses the browser when it is reached. The React Developer Tools browser extension complements this with the component tree, props and state, and its Suspense panel lists the <Suspense> boundaries of the page.

The DevTools MCP server

Next.js 16 added a Model Context Protocol endpoint, /_next/mcp, to the development server. The next-devtools-mcp package lets a coding agent discover the running server and call tools such as get_errors, get_logs, get_page_metadata, get_project_metadata and get_routes (added in 16.1), plus get_server_action_by_id, get_compilation_issues and compile_route. Register it in .mcp.json at the project root:

The .mcp.json of the official MCP guide
{
  "mcpServers": {
    "next-devtools": {
      "command": "npx",
      "args": ["-y", "next-devtools-mcp@latest"]
    }
  }
}

The server only exists while next dev runs, so restart the dev server if the agent cannot connect. Install the package only in projects where you want agents to see the running application.

Cross-origin dev access

Next.js blocks cross-origin requests to dev-only assets and endpoints. localhost, its subdomains and the hostname the server was started with are allowed; opening the app through another hostname (a tunnel, a custom domain, a different machine) needs an allowedDevOrigins entry containing only the hostname, without scheme or port. Without it the browser’s requests to those dev-only assets and endpoints are blocked.

next.config.ts: allowing a development tunnel
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  allowedDevOrigins: ['*.tunnel.example.com'],
}

export default nextConfig

A Tour of the App Router Getting-Started Sequence

The official documentation orders its App Router "Getting Started" chapter as a path. The table keeps that order and says what each step teaches and where it appears in this section. Pages of this section that are not written yet are named in plain text.

Table 9. The official getting-started sequence (docs 16.3.8)
Step Official page What it teaches and where this section goes deeper

1

Installation

Create a project, requirements, manual install, TypeScript, linting, module path aliases, next upgrade. This page.

2

Project Structure

Folder and file conventions, colocation, private folders, route groups. The Project Structure page of this section.

3

Layouts and Pages

Pages, layouts, nested routes, dynamic segments, search params, Link, PageProps. First pages above; the routing pages go deeper.

4

Linking and Navigating

Prefetching, prerendering, streaming and client-side transitions. Routing pages; see also React Routing for the client-only baseline.

5

Server and Client Components

Which code runs where, 'use client', composition. The first Client Component above.

6

Fetching Data

Fetching in Server Components, streaming with Suspense, loading.js.

7

Mutating Data

Server Functions and Server Actions, forms.

8

Caching

The caching model, including Cache Components.

9

Revalidating

Time-based and on-demand revalidation.

10

Error Handling

Expected errors versus uncaught exceptions, error.js, not-found.js.

11

CSS

Tailwind CSS, CSS Modules, global CSS; see also React Styling and UI Libraries.

12

Image Optimization

The next/image component.

13

Font Optimization

next/font, including self-hosted fonts.

14

Metadata and OG images

The Metadata API, file-based metadata, generated Open Graph images.

15

Route Handlers

HTTP endpoints built on the Web Request and Response APIs, as in the --api template above.

16

Proxy

proxy.ts, the 16 replacement of middleware.ts: rewrite, redirect, headers.

17

Deploying

Node.js server, Docker, static export, adapters; see also React Build and Deployment.

18

Upgrading

next upgrade, version guides, canary.

Read the chapter in order the first time: each page assumes the previous one, and the dependencies between them (layouts before navigation, Server and Client Components before data fetching, fetching before caching) are the same as in the Bookshelf that this section builds.

Editor, Linting, Aliases and Upgrades

Editor setup

The App Router names files by convention, so an editor fills with tabs called page.tsx. The official page suggests labelling each tab with its enclosing folders using VS Code 1.88 or later (or Cursor) custom editor labels; JetBrains IDEs already show the folder.

The custom editor labels (excerpt)
{
  "workbench.editor.customLabels.patterns": {
    "**/app/**/page.tsx": "${dirname(1)}/${dirname} - page.tsx",
    "**/app/**/layout.tsx": "${dirname(1)}/${dirname} - layout.tsx",
    "**/app/**/route.ts": "${dirname(1)}/${dirname} - route.ts"
  }
}

To use the Next.js TypeScript plugin in VS Code, run "TypeScript: Select TypeScript Version" and choose "Use Workspace Version".

Linting

Use ESLint (the default) or Biome through plain package.json scripts. In Next.js 16 next build does not run the linter and next lint is gone, so linting must be a step of your own in CI.

The two script sets
{ "scripts": { "lint": "eslint", "lint:fix": "eslint --fix" } }
{ "scripts": { "lint": "biome check", "format": "biome format --write" } }

Absolute imports and aliases

create-next-app writes the @/* alias for you. In a manual project, add paths (and optionally baseUrl) to tsconfig.json or jsconfig.json; Next.js reads both. With baseUrl set to src/, each paths target is relative to it.

tsconfig.json: a baseUrl and named aliases
{
  "compilerOptions": {
    "baseUrl": "src/",
    "paths": {
      "@/styles/*": ["styles/*"],
      "@/components/*": ["components/*"]
    }
  }
}

Upgrading

Upgrading Next.js and the bundled docs
npx next upgrade                                        # 16.1 and later
npx @next/codemod@canary upgrade latest                 # before 16.1
npm i next@latest react@latest react-dom@latest eslint-config-next@latest   # manually

Upgrading also updates the documentation under node_modules/next/dist/docs/, which is what the agent files point at. Versions before 16.1.0 cannot run next upgrade.

Source: Upgrading

Pitfalls

Table 10. Common first-day problems
Symptom Cause Fix

Every request returns 500 with "Specified module format (CommonJs) is not matching"

package.json contains "type": "commonjs" (written by npm init -y on npm 11.13)

Remove the field, or set it to "module".

next lint prints "Invalid project directory provided"

The command was removed in 16; next treats the word as the project directory

Use the eslint script; migrate with the next-lint-to-eslint-cli codemod.

next build fails after adding a webpack function to next.config

Turbopack is the default and the version 16 upgrade guide says a custom webpack config fails the build

Remove the customisation, or run next build --webpack and next dev --webpack.

Error: listen EADDRINUSE: address already in use :::3000

Another process owns the port

PORT=3001 npm run dev or npm run dev — -p 3001. PORT in .env is ignored.

The page loads through a tunnel or custom host but dev features do not work

The origin is not allowed in development

Add the hostname to allowedDevOrigins.

await is missing on params or searchParams and the page fails or warns

Since 15 they are Promises; 16 removed synchronous access

const { id } = await props.params, or use PageProps for the types.

The build fails with "uncached or runtime data during prerendering"

cacheComponents is on and request data is read outside <Suspense>

Move the read into a child under <Suspense>, cache it with 'use cache', or opt out of the shell with export const instant = false.

A missing book returns HTTP 200 instead of 404

notFound() was thrown inside a streamed <Suspense> boundary after the response had started

Call it before streaming begins, or add a noindex robots tag; the status cannot change after the headers are sent.

The first next build fails offline

The scaffold’s layout downloads Geist from Google Fonts

Allow the network, or self-host the font with next/font/local.

A change does not show after next start

next start serves the last build

Run next build again; use next dev while editing.

.env values are committed

The .env* ignore rule was removed

Keep .env* in .gitignore; only variables prefixed NEXT_PUBLIC_ are bundled into browser code.

Where the Books Are Outdated

The two books consulted for this section — Real-World Next.js (Michele Riva, Packt, 2022) and Practical Next.js for E-Commerce (Alex Libby, Apress, 2023) — target the Pages Router on Next.js 12 and 13.2, with React 17 and 18. Their first chapters are the setup and project-layout chapters, which is where the differences from today are largest. They are good for the concepts and wrong for the commands.

Table 11. Setup and tooling: book era against 16.3
Topic In the books Today (16.3.8)

Router in the template

pages/, with _app and _document

app/ with a root layout is the default and the recommended choice; the Pages Router is still supported (it has its own installation page) but is the legacy path for new work.

create-next-app

A short prompt list (TypeScript, ESLint, src/, alias) in which the App Router, stable only from 13.4, was not yet the default

A recommended-defaults choice, then optional prompts for linter (ESLint, Biome or none), React Compiler, Tailwind, src/, App Router, alias and AGENTS.md; all as flags.

Bundler

webpack, custom .babelrc and webpack config

SWC for transforms and Turbopack as the default for dev and build (16); a custom webpack function needs --webpack.

Linting

next lint, eslint key in the config, .eslintrc

next lint and the eslint config key were removed in 16; run eslint with the flat config (eslint.config.mjs).

next.config

next.config.js only, serverRuntimeConfig and publicRuntimeConfig

next.config.ts since 15 with the NextConfig type; the runtime-config keys were removed in 16.

Minimum Node.js

Node 14 or 16 (both end-of-life)

20.9.0 or later.

Request APIs and params

Synchronous req, query and params objects

params and searchParams are Promises; cookies() and headers() are asynchronous (15, synchronous access removed in 16).

Dev experience

Fast Refresh and the red error overlay

Fast Refresh, an overlay with Error.cause, a hydration diff, the dev indicator, DevTools panels and an MCP server.

next export

A command that wrote an out folder

The command was removed in 14; use output: 'export' in the config.

Starting from the framework

Comparisons with Gatsby, Razzle and Nuxt

Out of scope on this page; see the framework comparison on the Introduction & Architecture page of this section.

Where the books use a library or technique that is mentioned only briefly on this page — next/head, getStaticProps, getServerSideProps and a custom _app — the replacement is the Metadata API and Server Components, introduced in the first-page section above and covered on later pages.

What Changed Recently

The changes that affect getting started, by the version that introduced them.

Table 12. Setup and development changes, 13 to 16.3
Version Change

13.4

The App Router became stable.

15.0 (2024-10-21)

React 19 support; asynchronous request APIs; fetch and GET Route Handlers uncached by default; client router cache staleTimes.dynamic of 0; Turbopack for dev stable; next.config.ts; the static route indicator; create-next-app projects ignore all .env files by default.

15.2 (2025-02-26)

The improved dev indicator and its position option; the old indicator options deprecated.

16.0 (2025-10-21)

Turbopack the default for dev and build; next lint and the eslint config key removed; Node.js 20.9 minimum (18 dropped); middleware.ts replaced by proxy.ts; create-next-app redesigned (simpler flow, App Router, TypeScript, Tailwind and ESLint by default); the Next.js DevTools MCP; next dev output in .next/dev; dev and build can run concurrently; React Compiler support (reactCompiler) stable and off by default.

16.1 (2025-12-18)

next upgrade; next dev --inspect; stable Turbopack file-system cache for dev; next experimental-analyze (experimental); get_routes in the DevTools MCP.

16.2 (2026-03-18)

Hydration diff in the overlay; Error.cause in the overlay; next start --inspect; AGENTS.md generated by create-next-app; version-matched docs bundled in node_modules/next/dist/docs/; stable Adapters API.

16.3 (2026-08-03)

next dev maintains the AGENTS.md block in existing projects; Instant Navigations with cacheComponents and partialPrefetching; Instant Insights and the Navigation Inspector in the DevTools; up to 90 percent less memory in next dev through disk caching and memory eviction; ISR with an app shell for unknown params; TypeScript 7 type checking.

The support policy at the checked date: 16.x is Active LTS, 15.x is Maintenance LTS (security and critical fixes, with version 15.5.27 the latest backport), and 14.x and earlier are unsupported. A project started today should be on 16.

Where to Go Next

Read the Introduction & Architecture page of this section for what a React framework adds and how the App Router and Pages Router relate, and the Project Structure page for the complete file-convention table and the final Bookshelf layout. The Configuration, TypeScript and Environment Variables page covers next.config.ts, the TypeScript plugin and typed routes, .env* load order and NEXT_PUBLIC_ inlining. Those pages are written alongside this one and are named here in plain text so that no link points at a page that does not exist yet.

Beyond this section: React Getting Started for the library under Next.js, Server Rendering, SSG and React Server Components for the rendering ideas, NestJS vs Next.js for how a Next.js front end relates to a NestJS back end, and Django Getting Started for the equivalent first steps in a Python stack.