Next.js App Development
Use this capability whenever you write or review a Next.js application built on the App Router. It owns the framework's conventions: where files live and what their names mean to the router, which code runs on the server and which ships to the browser, how data is fetched and cached, how a mutation is exposed and secured, how errors surface, and how the app is configured, instrumented, bundled, tested, and deployed.
It carries both voices. Every reference states the authoring rule — what to write — and, where the surface has a known failure mode, the review check: what to look for in a diff and how severe it is when found. A reviewer loads this one skill to know what to review on a Next.js change; there is no separate review skill to pair it with.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Baseline and Scope
Next.js 16 is the baseline. Every rule here is written against v16 semantics, verified against the 16.2.x App Router documentation on 2026-08-02. No rule is qualified as holding only on 14 or 15 — where a v16 rule inverts an older one, the older behaviour appears solely in the arrival checklist in version-and-tooling.md.
The App Router is the only router in scope. The Pages Router — pages/, getServerSideProps, getStaticProps, API Routes — is out of scope entirely: no rule, no migration path, no comparison.
Experimental and canary APIs are taught, not hidden, each marked with the flag it requires and the channel it ships on, so a reader can weigh the risk deliberately rather than discover it in production.
No vendor SDK is named as required. Integrations are named by role — your auth provider, your error tracker, your analytics tool, your CMS — and a managed platform and a self-hosted pipeline are treated as two adapters of one deployment contract. Where a rule needs a concrete example, it is an example, not a dependency.
Boundaries
This skill stops where another capability owns the subject:
- A component's own mechanics — composition, props contracts, logic extraction, state placement, memoization, list virtualization — belong to a React component development capability. This skill governs only which side of the server/client boundary a component sits on and what that costs.
- CSS, design tokens, themes, container queries, and stylesheet structure belong to a React component styling capability.
- Log levels, capture semantics, breadcrumbs, and PII boundaries belong to a software instrumentation capability. This skill owns only the Next-specific wiring that hands those signals to it —
instrumentation.ts, onRequestError, instrumentation-client.ts.
- Assertion design, fixture quality, and coverage judgment belong to unit-testing, end-to-end-testing, and quality-assurance capabilities. This skill owns only what is Next-specific about testing an App Router app.
- The OWASP lens itself — injection, SSRF, secret handling, access-control modelling — belongs to an application security capability. This skill applies it to Next's own surfaces: server functions, route handlers, the proxy, image remote patterns, and environment inlining.
The host repository's existing convention wins. Every rule states a default for a project that has not decided yet. Where the surrounding codebase has already answered a question — the source root, barrel files, filename suffixes, the import alias — match what is there rather than migrating the codebase toward this skill as a side effect of an unrelated change.
Project Structure
See project-structure.md for:
- placing a source root and organizing domain directories beneath it, each with per-kind subdirectories
- keeping
app/ as thin route entrypoints that import from domain directories
- separating the
common/ and core/ cross-cutting tiers, and when a module earns a place in common/
- choosing the import alias, and the kebab-case file naming default
- deciding whether barrel files belong in this repository
- which files stay at the repository root even when a source root exists
- restructuring an existing tree without a rewrite
Route Files
See route-files.md for:
- the special files the router recognizes, including the experimental global not-found
- the order they nest in, and what each one wraps
- dynamic, catch-all, and optional catch-all segments; route groups, private folders, parallel slots, and intercepting routes
- colocating non-route files inside
app/, and segment naming
- the pairs that cannot share a directory, and the slot files a build now requires
Route Modules
See route-modules.md for:
- the default export every route module owes the router
- awaiting
params and searchParams, and the generated type helpers that describe them
- treating route parameters as untrusted input
- reading a route promise from a synchronous component
- choosing between a layout and a template, and what a layout does not re-render
- the streaming cost of awaiting request data at the top of a layout
Navigation
See navigation.md for:
- linking between routes, and when an imperative navigation is the right tool
- prefetching defaults, and turning prefetch down on a large or costly route
- surfacing pending state on a link
- the search-params hook that opts a route out of static rendering, and how to contain it
- typed routes and History API updates
- building a modal from parallel and intercepting routes
Component Boundaries
See component-boundaries.md for:
- the three categories a component can occupy, and the decision tree that picks one
- universal components as the default, and the four constraints that define them
- what belongs on the server as a positive catalogue, not just a prohibition
- placing the client boundary as low as possible, and what crosses it
- serializable props, interleaving, and why an async client component is invalid
- the two composition patterns this skill recommends, with the hydration mistakes they avoid
Directives
See directives.md for:
- the fence that keeps a module out of the client graph
- the directive that exposes a function as a callable endpoint, and why the two are not interchangeable
- the caching directive and its private and remote variants
- the client-side mirror of the server fence
- the read-only repository marked as a server module, named as an anti-pattern
Rendering and Streaming
See rendering-and-streaming.md for:
- what makes a route static or dynamic, and how
cacheComponents redraws that boundary
- the loading file, and what it costs to declare one
- choosing Suspense granularity, and when a boundary wants no fallback
- pregenerating dynamic segments
- deferring work until after the response is sent
Data Fetching
See data-fetching.md for:
- Server Components, route handlers with client fetching, or a server-state library — and why a codebase should not mix them
- a fenced data-access layer that returns transfer objects rather than rows
- removing waterfalls with parallel awaits, promise props, and preloading
- deduplicating a query that a page and its metadata both need
- fetching from the client, and where that leaves validation
Mutations
See mutations.md for:
- server functions as the default mutation path, and the form integration around them
- the three checks every server function performs before it writes
- keeping the exposed function thin over a fenced module
- what a mutation may return, and where navigation calls belong relative to error handling
- closure encryption, allowed origins, and mutating during render
- calling an external backend instead, and what that changes
Route Handlers
See route-handlers.md for:
- the method exports a route file recognizes, and the typed context helper
- when a handler is the right tool and when a server function is
- the environment functions supported inside a handler
- shaping a response — JSON, redirects, status and headers, streaming
- preferring Web-standard APIs while permitting Node.js ones
- cross-origin and replay protection on authenticated writes
- generating images from a handler
Caching
See caching.md for:
- applying the caching directive at route, component, and function level
- declaring a cache lifetime explicitly instead of inheriting the default profile
- why runtime request data cannot be read inside a cached scope, and the pattern that replaces it
- how a cache key is built, and the cardinality trap in closure capture
- tagging entries, and the three invalidation APIs and what distinguishes them
- path-based invalidation and why user input must never reach it
- wiring invalidation to the write that causes it
Error Handling
See error-handling.md for:
- the error boundary files, their scope, and the one that replaces the root layout
- the not-found files, including the experimental global variant
- the navigation interrupts, and the canary auth interrupts behind their flag
- how an error bubbles, and where a
try/catch belongs
- rethrowing framework control-flow errors that a
catch would otherwise swallow
- why a not-found interrupt is not an error to report
Metadata
See metadata.md for:
- static metadata versus the generated variant, and when each applies
- the base URL, title templates, robots directives, and Open Graph fields
- the viewport export that is no longer part of metadata
- the metadata file conventions the router picks up automatically
- sitemaps and robots files, including the sharded variant
- generating Open Graph images, and embedding structured data
Images, Fonts, and Assets
See images-fonts-and-assets.md for:
- the image component and the props that prevent layout shift
- scoping remote patterns and enumerating local ones
- the v16 image defaults that changed, and what each one breaks
- falling back to an unoptimized image
- loading fonts through the built-in loader instead of a stylesheet link
- the public directory and serving assets from a CDN prefix
Proxy
See proxy.md for:
proxy.ts beside app/, its proxy or default export, and the config object next to it
- the runtime it pins, and the documentation page that still disagrees
- writing a matcher, and what runs when you omit one
- what belongs in the proxy and what does not
- why it runs on prefetches and data routes
- why it is never the only authorization check
Authentication
See authentication.md for:
- session strategies and the cookie flags each one needs
- an optimistic check at the edge of the app versus the authoritative check at the data layer
- a deduplicated session verifier, and calling it in every server function and route handler
- the layout caveat that makes a layout check insufficient
- degrading gracefully when the auth provider is unconfigured
Internationalization
See internationalization.md for:
- a locale segment versus single-URL negotiation, and what each costs
- negotiating from the request header with quality values, and letting a cookie override it
- loading dictionaries on the server rather than shipping them
- locale metadata in the document and in Open Graph
- pregenerating locales, and how locale interacts with the cache key
Configuration and Environment
See configuration-and-environment.md for:
next.config.ts typed with NextConfig, and why cacheComponents, reactCompiler, and output change behaviour rather than tune it
- the default bundler and the config that makes a build fail
- environment file precedence, and the prefix that inlines a value into client bundles
- why an inlined variable must be read by its full literal name
- centralizing environment reads, and forcing a read to happen at runtime
- reading environment outside the running app, and the runtime config that no longer exists
Observability Wiring
See observability-wiring.md for:
- the server instrumentation file and splitting registration by runtime
- the request-error hook and what it receives
- client instrumentation and the router-transition hook
- the build plugin, source-map upload, tunnel, and release tagging an error tracker needs
- loading third-party scripts, and reporting web vitals
Bundling
See bundling.md for:
- why the client boundary is transitive, and how a server-only package reaches a client bundle
- externalizing a server package, and transpiling one that ships untranspiled
- barrels and namespace imports that defeat tree-shaking, and how
optimizePackageImports rewrites them
- dynamic imports, disabling server rendering on one, and the SEO trap that follows
- what the compiler changes about hand-written memoization
- the build-output metrics that no longer exist, and what to measure instead
Testing
See testing.md for:
- the transform and module mapper a unit runner needs for this framework
- why an async Server Component is not unit-testable, and what to cover instead
- the test-hook contract across loading and loaded states
- running the suite against dev, a local production build, and a deployed target
Deployment
See deployment.md for:
- what the application declares regardless of where it runs
- a managed platform and a self-hosted pipeline as two adapters of one contract
- the output modes and what each one gives up
- version skew across a deploy, and the identifier that pins it
- cache headers at a CDN, and the shape of a CI pipeline
Version and Tooling
See version-and-tooling.md for:
- reading documentation at the version the project actually runs
- the upgrade command and the codemods it applies
- the arrival checklist for a codebase coming from the previous major
- the runtime and language versions this major requires
- the DevTools MCP server and the in-app endpoint, and what they answer
- diagnosing which routes a build spent its time on
1---2name: next-app-development3description: Writing or reviewing a Next.js App Router change — the version 16 framework layer, covering routing, rendering, caching, mutations, and deployment. Triggers on "app router", "server component", "use client", "use cache", "server action", "route handler", "proxy.ts", "middleware", "generateMetadata", "revalidateTag", "next/image", "next.config", "instrumentation", "cacheComponents", an auth interrupt, a hydration or serialization error, or a route rendering dynamically when it should be static. For a component's own composition, props, and state use a React component development capability; for CSS and themes a React component styling one. Pages Router out of scope.4---56# Next.js App Development78Use this capability whenever you write or review a Next.js application built on the App Router. It owns **the framework's conventions**: where files live and what their names mean to the router, which code runs on the server and which ships to the browser, how data is fetched and cached, how a mutation is exposed and secured, how errors surface, and how the app is configured, instrumented, bundled, tested, and deployed.910It carries **both voices**. Every reference states the authoring rule — what to write — and, where the surface has a known failure mode, the review check: what to look for in a diff and how severe it is when found. A reviewer loads this one skill to know what to review on a Next.js change; there is no separate review skill to pair it with.1112The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html).1314## Baseline and Scope1516**Next.js 16 is the baseline.** Every rule here is written against v16 semantics, verified against the [16.2.x App Router documentation](https://nextjs.org/docs/app) on **2026-08-02**. No rule is qualified as holding only on 14 or 15 — where a v16 rule inverts an older one, the older behaviour appears solely in the arrival checklist in [version-and-tooling.md](./references/version-and-tooling.md).1718**The App Router is the only router in scope.** The Pages Router — `pages/`, `getServerSideProps`, `getStaticProps`, API Routes — is out of scope entirely: no rule, no migration path, no comparison.1920**Experimental and canary APIs are taught, not hidden**, each marked with the flag it requires and the channel it ships on, so a reader can weigh the risk deliberately rather than discover it in production.2122**No vendor SDK is named as required.** Integrations are named by role — your auth provider, your error tracker, your analytics tool, your CMS — and a managed platform and a self-hosted pipeline are treated as two adapters of one deployment contract. Where a rule needs a concrete example, it is an example, not a dependency.2324## Boundaries2526This skill stops where another capability owns the subject:2728- **A component's own mechanics** — composition, props contracts, logic extraction, state placement, memoization, list virtualization — belong to a React component development capability. This skill governs only which side of the server/client boundary a component sits on and what that costs.29- **CSS, design tokens, themes, container queries, and stylesheet structure** belong to a React component styling capability.30- **Log levels, capture semantics, breadcrumbs, and PII boundaries** belong to a software instrumentation capability. This skill owns only the Next-specific wiring that hands those signals to it — `instrumentation.ts`, `onRequestError`, `instrumentation-client.ts`.31- **Assertion design, fixture quality, and coverage judgment** belong to unit-testing, end-to-end-testing, and quality-assurance capabilities. This skill owns only what is Next-specific about testing an App Router app.32- **The OWASP lens itself** — injection, SSRF, secret handling, access-control modelling — belongs to an application security capability. This skill applies it to Next's own surfaces: server functions, route handlers, the proxy, image remote patterns, and environment inlining.3334**The host repository's existing convention wins.** Every rule states a default for a project that has not decided yet. Where the surrounding codebase has already answered a question — the source root, barrel files, filename suffixes, the import alias — match what is there rather than migrating the codebase toward this skill as a side effect of an unrelated change.3536## Project Structure3738See [project-structure.md](./references/project-structure.md) for:3940- placing a source root and organizing domain directories beneath it, each with per-kind subdirectories41- keeping `app/` as thin route entrypoints that import from domain directories42- separating the `common/` and `core/` cross-cutting tiers, and when a module earns a place in `common/`43- choosing the import alias, and the kebab-case file naming default44- deciding whether barrel files belong in this repository45- which files stay at the repository root even when a source root exists46- restructuring an existing tree without a rewrite4748## Route Files4950See [route-files.md](./references/route-files.md) for:5152- the special files the router recognizes, including the experimental global not-found53- the order they nest in, and what each one wraps54- dynamic, catch-all, and optional catch-all segments; route groups, private folders, parallel slots, and intercepting routes55- colocating non-route files inside `app/`, and segment naming56- the pairs that cannot share a directory, and the slot files a build now requires5758## Route Modules5960See [route-modules.md](./references/route-modules.md) for:6162- the default export every route module owes the router63- awaiting `params` and `searchParams`, and the generated type helpers that describe them64- treating route parameters as untrusted input65- reading a route promise from a synchronous component66- choosing between a layout and a template, and what a layout does not re-render67- the streaming cost of awaiting request data at the top of a layout6869## Navigation7071See [navigation.md](./references/navigation.md) for:7273- linking between routes, and when an imperative navigation is the right tool74- prefetching defaults, and turning prefetch down on a large or costly route75- surfacing pending state on a link76- the search-params hook that opts a route out of static rendering, and how to contain it77- typed routes and History API updates78- building a modal from parallel and intercepting routes7980## Component Boundaries8182See [component-boundaries.md](./references/component-boundaries.md) for:8384- the three categories a component can occupy, and the decision tree that picks one85- universal components as the default, and the four constraints that define them86- what belongs on the server as a positive catalogue, not just a prohibition87- placing the client boundary as low as possible, and what crosses it88- serializable props, interleaving, and why an async client component is invalid89- the two composition patterns this skill recommends, with the hydration mistakes they avoid9091## Directives9293See [directives.md](./references/directives.md) for:9495- the fence that keeps a module out of the client graph96- the directive that exposes a function as a callable endpoint, and why the two are not interchangeable97- the caching directive and its private and remote variants98- the client-side mirror of the server fence99- the read-only repository marked as a server module, named as an anti-pattern100101## Rendering and Streaming102103See [rendering-and-streaming.md](./references/rendering-and-streaming.md) for:104105- what makes a route static or dynamic, and how `cacheComponents` redraws that boundary106- the loading file, and what it costs to declare one107- choosing Suspense granularity, and when a boundary wants no fallback108- pregenerating dynamic segments109- deferring work until after the response is sent110111## Data Fetching112113See [data-fetching.md](./references/data-fetching.md) for:114115- Server Components, route handlers with client fetching, or a server-state library — and why a codebase should not mix them116- a fenced data-access layer that returns transfer objects rather than rows117- removing waterfalls with parallel awaits, promise props, and preloading118- deduplicating a query that a page and its metadata both need119- fetching from the client, and where that leaves validation120121## Mutations122123See [mutations.md](./references/mutations.md) for:124125- server functions as the default mutation path, and the form integration around them126- the three checks every server function performs before it writes127- keeping the exposed function thin over a fenced module128- what a mutation may return, and where navigation calls belong relative to error handling129- closure encryption, allowed origins, and mutating during render130- calling an external backend instead, and what that changes131132## Route Handlers133134See [route-handlers.md](./references/route-handlers.md) for:135136- the method exports a route file recognizes, and the typed context helper137- when a handler is the right tool and when a server function is138- the environment functions supported inside a handler139- shaping a response — JSON, redirects, status and headers, streaming140- preferring Web-standard APIs while permitting Node.js ones141- cross-origin and replay protection on authenticated writes142- generating images from a handler143144## Caching145146See [caching.md](./references/caching.md) for:147148- applying the caching directive at route, component, and function level149- declaring a cache lifetime explicitly instead of inheriting the default profile150- why runtime request data cannot be read inside a cached scope, and the pattern that replaces it151- how a cache key is built, and the cardinality trap in closure capture152- tagging entries, and the three invalidation APIs and what distinguishes them153- path-based invalidation and why user input must never reach it154- wiring invalidation to the write that causes it155156## Error Handling157158See [error-handling.md](./references/error-handling.md) for:159160- the error boundary files, their scope, and the one that replaces the root layout161- the not-found files, including the experimental global variant162- the navigation interrupts, and the canary auth interrupts behind their flag163- how an error bubbles, and where a `try`/`catch` belongs164- rethrowing framework control-flow errors that a `catch` would otherwise swallow165- why a not-found interrupt is not an error to report166167## Metadata168169See [metadata.md](./references/metadata.md) for:170171- static metadata versus the generated variant, and when each applies172- the base URL, title templates, robots directives, and Open Graph fields173- the viewport export that is no longer part of metadata174- the metadata file conventions the router picks up automatically175- sitemaps and robots files, including the sharded variant176- generating Open Graph images, and embedding structured data177178## Images, Fonts, and Assets179180See [images-fonts-and-assets.md](./references/images-fonts-and-assets.md) for:181182- the image component and the props that prevent layout shift183- scoping remote patterns and enumerating local ones184- the v16 image defaults that changed, and what each one breaks185- falling back to an unoptimized image186- loading fonts through the built-in loader instead of a stylesheet link187- the public directory and serving assets from a CDN prefix188189## Proxy190191See [proxy.md](./references/proxy.md) for:192193- `proxy.ts` beside `app/`, its `proxy` or default export, and the `config` object next to it194- the runtime it pins, and the documentation page that still disagrees195- writing a matcher, and what runs when you omit one196- what belongs in the proxy and what does not197- why it runs on prefetches and data routes198- why it is never the only authorization check199200## Authentication201202See [authentication.md](./references/authentication.md) for:203204- session strategies and the cookie flags each one needs205- an optimistic check at the edge of the app versus the authoritative check at the data layer206- a deduplicated session verifier, and calling it in every server function and route handler207- the layout caveat that makes a layout check insufficient208- degrading gracefully when the auth provider is unconfigured209210## Internationalization211212See [internationalization.md](./references/internationalization.md) for:213214- a locale segment versus single-URL negotiation, and what each costs215- negotiating from the request header with quality values, and letting a cookie override it216- loading dictionaries on the server rather than shipping them217- locale metadata in the document and in Open Graph218- pregenerating locales, and how locale interacts with the cache key219220## Configuration and Environment221222See [configuration-and-environment.md](./references/configuration-and-environment.md) for:223224- `next.config.ts` typed with `NextConfig`, and why `cacheComponents`, `reactCompiler`, and `output` change behaviour rather than tune it225- the default bundler and the config that makes a build fail226- environment file precedence, and the prefix that inlines a value into client bundles227- why an inlined variable must be read by its full literal name228- centralizing environment reads, and forcing a read to happen at runtime229- reading environment outside the running app, and the runtime config that no longer exists230231## Observability Wiring232233See [observability-wiring.md](./references/observability-wiring.md) for:234235- the server instrumentation file and splitting registration by runtime236- the request-error hook and what it receives237- client instrumentation and the router-transition hook238- the build plugin, source-map upload, tunnel, and release tagging an error tracker needs239- loading third-party scripts, and reporting web vitals240241## Bundling242243See [bundling.md](./references/bundling.md) for:244245- why the client boundary is transitive, and how a server-only package reaches a client bundle246- externalizing a server package, and transpiling one that ships untranspiled247- barrels and namespace imports that defeat tree-shaking, and how `optimizePackageImports` rewrites them248- dynamic imports, disabling server rendering on one, and the SEO trap that follows249- what the compiler changes about hand-written memoization250- the build-output metrics that no longer exist, and what to measure instead251252## Testing253254See [testing.md](./references/testing.md) for:255256- the transform and module mapper a unit runner needs for this framework257- why an async Server Component is not unit-testable, and what to cover instead258- the test-hook contract across loading and loaded states259- running the suite against dev, a local production build, and a deployed target260261## Deployment262263See [deployment.md](./references/deployment.md) for:264265- what the application declares regardless of where it runs266- a managed platform and a self-hosted pipeline as two adapters of one contract267- the output modes and what each one gives up268- version skew across a deploy, and the identifier that pins it269- cache headers at a CDN, and the shape of a CI pipeline270271## Version and Tooling272273See [version-and-tooling.md](./references/version-and-tooling.md) for:274275- reading documentation at the version the project actually runs276- the upgrade command and the codemods it applies277- the arrival checklist for a codebase coming from the previous major278- the runtime and language versions this major requires279- the DevTools MCP server and the in-app endpoint, and what they answer280- diagnosing which routes a build spent its time on