vercel/next.js
App Router
The React Server Components-based router rooted at the app/ directory in user code. Introduced in March 2023, the App Router is the framework's default for new projects and the focus of most ongoing investment. It supports streaming, server actions, partial prerendering (PPR), and the cache components primitive.
Two halves
graph LR
subgraph Server
AppRender["app-render.tsx<br/>~7,000 lines"]
EntryBase["entry-base.ts<br/>RSDW boundary"]
ActionHandler["action-handler.ts<br/>50KB"]
WorkStorage["work-async-storage.external.ts"]
end
subgraph Client
AppRouter["app-router.tsx"]
LayoutRouter["layout-router.tsx"]
Reducer["router-reducer/"]
SegmentCache["segment-cache/"]
end
User[Browser] -- HTTP --> AppRender
AppRender -- RSC stream --> User
User -- prefetch / nav --> AppRouter
AppRouter --> Reducer
Reducer --> SegmentCacheServer side
app-render.tsx
packages/next/src/server/app-render/app-render.tsx is the entry point — the largest single file in the codebase at 8,759 lines. It does:
- Read the work async storage to populate per-request context (URL, params, cookies, headers).
- Build the loader tree from the route match (the segment hierarchy: layouts, pages, error boundaries, loading boundaries).
- Render the React Server Components tree, encoding it as a Flight payload via
react-server-dom-webpack/server(orreact-server-dom-turbopack/serverfor Turbopack builds). - Pipe the SSR HTML output, interleaving Flight chunks for streaming.
- If PPR is enabled, postpone-track dynamic boundaries and serialize the postponed state.
- Run the after-task queue (
unstable_after).
The function deliberately interleaves all of these because React's RSC + SSR + postpone APIs need a single render cycle.
entry-base.ts
packages/next/src/server/app-render/entry-base.ts is the only file in the framework allowed to import from react-server-dom-webpack/*. Every other file accesses these via re-exports here. The reason: Turbopack swaps the import to react-server-dom-turbopack at module-resolution time, and a single boundary file makes that swap surgical.
Loader tree
The "loader tree" is the data structure that mirrors the App Router segment hierarchy. Each node has a layout, optionally a page, error boundaries, loading boundaries, and templates. Helpers:
packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx— walks the loader tree applying the client's router state.packages/next/src/server/app-render/create-component-tree.tsx(47 KB) — turns loader tree segments into actual React component trees.packages/next/src/server/app-render/create-flight-router-state-from-loader-tree.ts— produces the router state used by the client.
Async storages used during a render
| Storage | Holds |
|---|---|
work-async-storage.external.ts |
Per-request work context (URL, params, etc.) |
work-unit-async-storage.external.ts |
Per-render-unit context (cache scopes) |
action-async-storage.external.ts |
Active server action invocation |
after-task-async-storage.external.ts |
Queued unstable_after tasks |
console-async-storage.external.ts |
Console capture for replay |
dynamic-access-async-storage.external.ts |
Dynamic API access tracking for PPR |
PPR (Partial Prerendering)
packages/next/src/server/app-render/postponed-state.ts and dynamic-rendering.ts (50 KB) handle the postponed-state machinery. When a render encounters a dynamic API (e.g., headers(), cookies(), searchParams), the React tree at that point is "postponed" — the static prefix is committed and the dynamic suffix is deferred until request time.
When __NEXT_CACHE_COMPONENTS=true is set, most app-dir pages enable PPR implicitly. The legacy ppr-full/ and ppr/ test suites are mostly describe.skip while the migration to cache components completes.
Client side
app-router.tsx
packages/next/src/client/components/app-router.tsx (23 KB) is the React Provider that wraps the app-dir tree on the client. It sets up:
- The router instance (
app-router-instance.ts) - The async-action queue (
use-action-queue.ts) - The error boundary (
error-boundary.tsx) - The redirect boundary (
redirect-boundary.tsx) - The
<RouteAnnouncer>for accessibility
layout-router.tsx
layout-router.tsx (31 KB) is the per-segment component that renders the appropriate layout, page, error boundary, or loading boundary based on the router state. It lazily fetches Flight payloads for new segments via the segment cache.
router-reducer
packages/next/src/client/components/router-reducer/ is the state machine for navigation. The big file is ppr-navigations.ts (2,061 lines) which handles PPR-aware navigation: when a navigation lands on a postponed page, the client must replay the postponed state with the new dynamic data.
Segment cache
packages/next/src/client/components/segment-cache/cache.ts (3,112 lines) is the client-side cache of fetched segments. It deduplicates prefetches, handles BFCache state via bfcache-state-manager.ts, and reconciles cache entries on navigation.
Links and prefetch
packages/next/src/client/components/links.ts is the client implementation backing <Link>. It implements:
- IntersectionObserver-based prefetch on viewport entry.
- Prefetch on hover (configurable).
- Click handling that translates to a router push.
- BFCache-aware prefetch invalidation.
The next/link server component lives at packages/next/src/client/link.tsx.
Streaming integration
The stream-ops files at packages/next/src/server/app-render/stream-ops.{ts,node.ts,web.ts} provide the per-runtime stream stitching:
- Node runtime (
stream-ops.node.ts, 32 KB) — uses Node's stream piping to interleave Flight chunks and HTML. - Web runtime (
stream-ops.web.ts) — uses Web Streams (ReadableStream / TransformStream). - The shared file
stream-ops.tsre-exports the right one based on the runtime.
Server actions integration
Server actions are App-Router-only. The handler at packages/next/src/server/app-render/action-handler.ts (51 KB) processes form posts and direct calls. See server actions.
Integration points
- Imports from
react-server-dom-webpack/*viaentry-base.tsonly. - Reads the loader tree built by
packages/next/src/build/route-discovery.ts. - Reads page manifests from
.next/app-build-manifest.jsonetc. - Writes Flight payloads consumed by the client app-router.
Entry points for modification
- To change a server-side render behavior:
packages/next/src/server/app-render/app-render.tsxor one of its helpers. - To add a new dynamic API: define it in
packages/next/src/server/request/, register tracking indynamic-rendering.ts. - To change client navigation:
packages/next/src/client/components/router-reducer/. - To change segment caching:
packages/next/src/client/components/segment-cache/cache.ts.
Key source files
| File | Purpose |
|---|---|
packages/next/src/server/app-render/app-render.tsx |
Entry render function |
packages/next/src/server/app-render/entry-base.ts |
RSDW import boundary |
packages/next/src/server/app-render/create-component-tree.tsx |
Loader-tree → React tree |
packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx |
Apply client router state to tree |
packages/next/src/server/app-render/dynamic-rendering.ts |
Dynamic API tracking + PPR postpone |
packages/next/src/server/app-render/postponed-state.ts |
PPR postponed-state serialization |
packages/next/src/server/app-render/stream-ops.node.ts |
Node streaming pipe |
packages/next/src/client/components/app-router.tsx |
Client provider |
packages/next/src/client/components/layout-router.tsx |
Per-segment client renderer |
packages/next/src/client/components/router-reducer/ppr-navigations.ts |
PPR-aware navigation state |
packages/next/src/client/components/segment-cache/cache.ts |
Client segment cache |
packages/next/src/client/components/links.ts |
<Link> client implementation |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.