---
title: "Client App Router"
description: "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, ..."
last_updated: "2026-09-23T10:52:03.131764+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/client-app-router"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx)
- [packages/next/src/client/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/index.tsx)
- [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx)
- [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx)
- [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts)
- [packages/next/src/client/app-next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next.ts)
- [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts)
- [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts)
- [packages/next/src/client/app-next-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next-turbopack.ts)
- [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts)
- [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx)
- [packages/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next-devtools/userspace/app/segment-explorer-node.tsx)
- [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx)
- [packages/next/src/client/app-next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next-dev.ts)
- [packages/next/src/client/app-globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-globals.ts)
- [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next-devtools/dev-overlay.browser.tsx)
- [packages/next/src/client/next-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-turbopack.ts)
- [packages/next/src/client/page-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/page-bootstrap.ts)
- [packages/next/src/shared/lib/app-router-context.shared-runtime.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/app-router-context.shared-runtime.ts)
- [packages/next/src/server/app-render/entry-base.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts)
- [packages/next/src/server/app-render/instant-validation/instant-config.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx)
- [packages/next/src/client/app-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-bootstrap.ts)
- [packages/next/src/client/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next.ts)
- [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts)
- [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/app/layout.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/app/layout.ts)
- [packages/next/src/client/next-dev-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev-turbopack.ts)
- [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/app/layout.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/app/layout.ts)
- [packages/next/src/client/components/client-page.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/client-page.tsx)
- [packages/next/src/client/components/client-segment.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/client-segment.tsx)
- [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts)
</details>

## Overview

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.

```mermaid
flowchart TD
  Bootstrap["appBootstrap() / initialize()"] --> Stream["Read Flight Stream (ReadableStream)"]
  Stream --> Payload["Initial RSC Payload Decoded"]
  Payload --> AppRouter["AppRouter Component (<AppRouter>)"]
  AppRouter --> ActionQueue["Mutable Action Queue & useActionQueue"]
  ActionQueue --> LayoutRouter["Nested LayoutRouters (<InnerLayoutRouter>)"]
  LayoutRouter --> History["HistoryUpdater & window.history synchronization"]
```

Sources: [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L184-L274), [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L59-L112)

---

## Initialization and Hydration Pipeline

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.

```mermaid
sequenceDiagram
  participant Bootstrap as appBootstrap
  participant Stream as ReadableStream
  participant Payload as createFromReadableStream
  participant Hydrate as hydrate()
  participant Router as AppRouter

  Bootstrap->>Stream: Register writer & consume __next_f chunks
  Stream->>Payload: Pipe stream into Flight decoder
  Payload-->>Hydrate: Resolve initialServerResponse (InitialRSCPayload)
  Hydrate->>Router: Mount <AppRouter> with action queue & initial state
```

Sources: [packages/next/src/client/app-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-bootstrap.ts#L58-L81), [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L184-L274), [packages/next/src/client/app-next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next.ts#L10-L18)

> [!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](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L194-L206)

---

## State Management and Action Queue

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.

```typescript
export function createMutableActionQueue(
  initialState: AppRouterState,
  instrumentationHooks: ClientInstrumentationHooks | null
): AppRouterActionQueue {
  const actionQueue: AppRouterActionQueue = {
    state: initialState,
    dispatch: (payload: ReducerActions, setState: DispatchStatePromise) =>
      dispatchAction(actionQueue, payload, setState),
    action: async (state: AppRouterState, action: ReducerActions) => {
      const result = reducer(state, action)
      return result
    },
    pending: null,
    last: null,
    onRouterTransitionStart:
      instrumentationHooks !== null &&
      typeof instrumentationHooks.onRouterTransitionStart === 'function'
        ? instrumentationHooks.onRouterTransitionStart
        : null,
  }
  return actionQueue
}
```

Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L220-L240), [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L61-L101)

> [!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](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L33-L40)

---

## Layout Routers and Segment Rendering

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.

```typescript
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)
  const bfcacheIdNumber = layout?.parentCacheNode.bfcacheId ?? 0
  return useMemo<AppRouterInstance>(
    () => ({
      back: router.back,
      forward: router.forward,
      refresh: router.refresh,
      // ... push, replace, prefetch, bfcacheId
      bfcacheId: String(bfcacheIdNumber),
    }),
    [router, bfcacheIdNumber]
  )
}
```

Sources: [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L176-L200), [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L678-L690)

---

## Browser History Synchronization

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.

```mermaid
flowchart LR
  State["appRouterState changed"] --> Insertion["useInsertionEffect in HistoryUpdater"]
  Insertion --> Check{"pushRef.pendingPush && href !== canonicalUrl?"}
  Check -- Yes --> Push["window.history.pushState() & clear pendingPush"]
  Check -- No --> Replace["window.history.replaceState()"]
  Push --> Commit["setLastCommittedTree(tree)"]
  Replace --> Commit
  Commit --> Ping["pingVisibleLinks() for prefetch re-validation"]
```

Sources: [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L59-L112)

> [!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](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L79-L86)

---

## Public Navigation API and Actions

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.

| Method | Signature | Behavior |
| :--- | :--- | :--- |
| `push` | `(href: string, options?: NavigateOptions) => void` | Navigates to `href`, pushing a new history entry and scrolling by default. |
| `replace` | `(href: string, options?: NavigateOptions) => void` | Navigates to `href`, replacing the current history entry. |
| `refresh` | `() => void` | Dispatches `ACTION_REFRESH` to fetch fresh server data for the current route tree. |
| `prefetch` | `(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). |

Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L391-L501), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L33-L90)

---

## Design Trade-Offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| **External Action Queue** (`createMutableActionQueue`) | Decouples router state transitions from React component lifecycles, enabling async prefetching and non-blocking dispatches. | Requires manual synchronization (`useActionQueue`, `useOptimistic`) to bridge external state into React renders. |
| **Segment Cache & Bfcache Activity** | Preserves component state across navigations for instant back/forward transitions. | Higher client memory consumption due to retained DOM nodes and cached Flight trees. |
| **Asynchronous Params Unwrapping** | Prevents synchronous blocking during server-side rendering and static shell generation. | Requires developers to unwrap `params` via `await` or `React.use()` in client and server components. |

Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L220-L256), [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L683-L690), [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L871-L881)

## Related

- [Router State Reducer](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/router-state-reducer)
- [Client Segment Cache](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/client-segment-cache)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
