Architecture Overview
Server Runtime
Rendering Pipeline
Client Navigation
Caching and Export
Development Tools
Build and Configuration
Ecosystem Packages
Testing Infrastructure
How It Works
The following files were used as context for generating this wiki page:
The Client App Router subsystem serves as the core client-side orchestration engine for Next.js App Router applications. It manages the lifecycle of React Server Component (RSC) payload ingestion, hydration, segment tree reconciliation, browser history synchronization, and navigation actions. By decoupling router state transitions from React component trees via an external action queue and leveraging React's concurrent features, the Client App Router enables partial prerendering (PPR), segment caching, instant navigations, and resilient state preservation across layout boundaries.
The architecture addresses the fundamental challenge of rendering and updating nested server components on the client without forcing full-page reloads. It coordinates between client-side navigation APIs (useRouter, usePathname, useSearchParams), layout nesting (LayoutRouter), and server-driven React Server DOM (Flight) stream decoding. Through fine-grained cache nodes and back-forward cache (bfcache) management, it ensures that shared layouts retain stable identities and internal states while leaf pages transition smoothly.
Sources: packages/next/src/client/app-index.tsx, packages/next/src/client/components/app-router.tsx
The client runtime initializes through entry points such as app-next.ts or app-next-turbopack.ts, which invoke appBootstrap and hydrate. Before any React components mount, appBootstrap executes any pending scripts in sequence (such as inline polyfills or beforeInteractive scripts). Concurrently, the Flight data stream is established via __next_f buffered chunks or direct stream reads.
Sources: packages/next/src/client/app-bootstrap.ts, packages/next/src/client/app-index.tsx, packages/next/src/client/app-next.ts
Note
When cache components and experimental cached navigations are enabled, the initial Flight stream is teed using readable.tee() so that a clone can be truncated at static stage boundaries for caching.
Sources: packages/next/src/client/app-index.tsx
State within the Client App Router does not live directly inside standard React useState hooks at the root level; instead, it resides in a mutable action queue created by createMutableActionQueue. The useActionQueue hook bridges this external mutable state with React by maintaining a canonical state via React.useState and wrapping it with useOptimistic to support gesture transitions and pending navigations.
When an action is dispatched (via dispatchAppRouterAction or dispatchNavigateAction), it flows through the action queue. In development mode, nextDispatch wraps the action with the development rendering indicator to visually reflect server renders and route transitions.
export function createMutableActionQueue(
initialState: AppRouterState,
instrumentationHooks: ClientInstrumentationHooks | null
): AppRouterActionQueue {
const actionQueue: AppRouterActionQueue = {
state: initialState,
dispatch
Sources: packages/next/src/client/components/app-router-instance.ts, packages/next/src/client/components/use-action-queue.ts
Warning
Dispatched actions before router initialization throw an internal error: Internal Next.js error: Router action dispatched before initialization. Ensure all components interacting with dispatchAppRouterAction mount strictly inside the <AppRouter> tree.
Sources: packages/next/src/client/components/use-action-queue.ts
Layout and page segments are orchestrated hierarchically via LayoutRouter components (InnerLayoutRouter and OuterLayoutRouter). At each level of the route tree, the router renders the active segment alongside historical segments preserved within hidden React <Activity> boundaries to enable instant back/forward transitions and bfcache restoration.
The LayoutRouterContext supplies downstream components with contextual boundaries, parent parameters, active cache nodes, and bfcache identifiers (bfcacheIdNumber), formatted with a b prefix (e.g. _r_0_) to mirror React's useId() and prevent collisions when concatenating keys.
export function useRouter(): AppRouterInstance {
const router = useContext(AppRouterContext)
if (router === null) {
throw new Error('invariant expected app router to be mounted')
}
const layout = useContext(LayoutRouterContext)
Sources: packages/next/src/client/components/navigation.ts, packages/next/src/client/components/layout-router.tsx
The HistoryUpdater component, embedded within <Router>, uses a useInsertionEffect to synchronize the app router's internal state with window.history. It captures the current Flight router tree and rendered search parameters, builds an AppHistoryState object, and injects it into window.history.state under the __PRIVATE_NEXTJS_INTERNALS_TREE property with the __NA: true identifier flag.
Sources: packages/next/src/client/components/app-router.tsx
Important
The history state distinguishes Next.js App Router entries from Pages Router entries and external history states via the __NA: true property. If __NA is absent, app-router history restoration handlers ignore the popstate event.
Sources: packages/next/src/client/components/app-router.tsx
The public router instance (publicAppRouterInstance) provides methods exposed to client components through useRouter(). All navigation mutations (push, replace, refresh) wrap their execution in startTransition to integrate with React's concurrent rendering model.
Sources: packages/next/src/client/components/app-router-instance.ts, packages/next/src/shared/lib/app-router-context.shared-runtime.ts
Sources: packages/next/src/client/components/app-router-instance.ts, packages/next/src/client/components/layout-router.tsx, packages/next/src/server/request/params.ts
ACTION_REFRESHprefetch | (href: string, options?: PrefetchOptions) => void | Prefetches href into the Segment Cache using PPR or Full strategies. |
back | () => void | Invokes window.history.back(). |
forward | () => void | Invokes window.history.forward(). |
hmrRefresh | () => void | Resets known routes and triggers an HMR refresh (development only). |
paramsawaitReact.use()