---
title: "Router State Reducer"
description: "The router state reducer manages asynchronous client-side navigations, state patches, and history traversals for the Next.js App Router by maintaining a centralized action queue and dispatch switch..."
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/router-state-reducer"
---

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

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

- [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.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/components/router-reducer/router-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts)
- [packages/next/src/client/components/segment-cache/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts)
- [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts)
- [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts)
- [packages/next/src/client/components/router-reducer/router-reducer-types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts)
- [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.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/components/router-reducer/reducers/hmr-refresh-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts)
- [packages/next/src/client/components/segment-cache/scheduler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts)
- [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts)
- [packages/next/src/client/components/router-reducer/create-initial-router-state.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts)
- [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts)
- [packages/next/src/client/components/segment-cache/optimistic-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts)
- [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/next-devtools/dev-overlay/shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/shared.ts)
- [packages/next/src/client/components/router-reducer/compute-changed-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.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)
</details>

## Overview

The router state reducer manages asynchronous client-side navigations, state patches, and history traversals for the Next.js App Router by maintaining a centralized action queue and dispatch switchboard. It processes incoming actions—such as client navigations, server-driven patches, page refreshes, hot-module reloads, server actions, and history restorations—to update the global application state, coordinate segment cache invalidations, and synchronize browser history entries. By decoupling the router state from React and coordinating transitions through specialized reducer modules, the system ensures reliable tree reconciliation, scroll and focus management, and seamless partial prerendering (PPR) support across user interactions.

Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:203-250](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L203-L250), [packages/next/src/client/components/app-router-instance.ts:95-144](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L95-L144)

## Action Dispatch Architecture and Queue

The Action Dispatch Architecture handles incoming navigation intents, history restorations, and data mutations by routing them through `useActionQueue` and scheduling them in an `AppRouterActionQueue`. Because the app router state lives outside React, actions are queued sequentially and dispatched to the main `clientReducer` switchboard, which delegates to specialized reducers based on action types.

Sources: [packages/next/src/client/components/app-router-instance.ts:44-144](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L44-L144), [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58), [packages/next/src/client/components/use-action-queue.ts:12-16](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L12-L16)

Actions flow through a sequence of functions that manage asynchronous execution and queue priority. When an action is dispatched, `dispatchAction()` evaluates the current queue state:
1. `dispatchAction()` creates a deferred promise for asynchronous actions (unless the action type is `ACTION_RESTORE`) and constructs an `ActionQueueNode`.
2. If `actionQueue.pending` is `null`, the action runs immediately via `runAction()`.
3. If an action is already pending and the incoming payload is an `ACTION_NAVIGATE` or `ACTION_RESTORE`, the current pending action is marked as `discarded = true`, its `.next` pointer is preserved, and the navigation starts immediately via `runAction()`.
4. Other action types are appended to `actionQueue.last` and scheduled for execution after preceding actions finish.
5. `runAction()` executes `actionQueue.action(prevState, payload)`, handling promises and invoking `handleResult()` or `runRemainingActions()`.
6. `runRemainingActions()` advances `actionQueue.pending` to the next node in the queue and triggers the next action, or checks if `actionQueue.needsRefresh` is set to dispatch an `ACTION_REFRESH`.

Sources: [packages/next/src/client/components/app-router-instance.ts:71-215](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L71-L215)

> [!WARNING]
> Navigations and restore actions (`ACTION_NAVIGATE` or `ACTION_RESTORE`) take immediate precedence over pending background actions by setting `actionQueue.pending.discarded = true`. Discarded actions that revalidated data will automatically trigger a deferred refresh via `actionQueue.needsRefresh` once remaining actions complete.

Sources: [packages/next/src/client/components/app-router-instance.ts:113-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L113-L127), [packages/next/src/client/components/app-router-instance.ts:191-198](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L191-L198)

The `clientReducer` function acts as the central router switchboard, matching incoming `action.type` strings against known action constants and delegating to specialized reducer functions. If environment checks or unknown action types are encountered, specific branches or errors are thrown.

Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58)

| Action Constant | Switch Case Handler | Target Module / Behavior | Sources |
| :--- | :--- | :--- | :--- |
| `ACTION_NAVIGATE` | `navigateReducer(state, action)` | Handles client-side navigation tasks and prefetch resolution. | [packages/next/src/client/components/router-reducer/router-reducer.ts:28-30](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L28-L30) |
| `ACTION_SERVER_PATCH` | `serverPatchReducer(state, action)` | Applies server-driven Flight router state patches. | [packages/next/src/client/components/router-reducer/router-reducer.ts:31-33](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L31-L33) |
| `ACTION_RESTORE` | `restoreReducer(state, action)` | Restores route states during history traversal (`popstate`). | [packages/next/src/client/components/router-reducer/router-reducer.ts:34-36](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L34-L36) |
| `ACTION_REFRESH` | `refreshReducer(state, action)` | Refreshes the current route and revalidates data segments. | [packages/next/src/client/components/router-reducer/router-reducer.ts:37-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L37-L39) |
| `ACTION_HMR_REFRESH` | `hmrRefreshReducer(state)` | Development-only HMR refresh; throws an error in production. | [packages/next/src/client/components/router-reducer/router-reducer.ts:40-50](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L40-L50) |
| `ACTION_SERVER_ACTION` | `serverActionReducer(state, action)` | Executes server actions and parses resulting Flight patches. | [packages/next/src/client/components/router-reducer/router-reducer.ts:51-53](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L51-L53) |

Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58)

> [!NOTE]
> On the server side, `reducer` evaluates to `serverReducer`, which is a noop function that immediately returns the incoming state unchanged, enabling better tree-shaking for server-side bundles.

Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:60-70](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L60-L70)

## Initial Router State Construction

Client-side router state initialization begins with `createInitialRouterState`, which processes the `InitialRSCPayload` delivered during server-side rendering or initial document load. This function extracts payload fields such as canonical URL parts, Flight data, rendered search queries, prefetch streams, and dynamic stale times to construct the initial `AppRouterState`.

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:25-55](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L25-L55)

```mermaid
graph TD
    A[InitialRSCPayload] --> B[Extract Flight Data & Tree]
    B --> C[convertRootFlightRouterStateToRouteTree]
    C --> D[createInitialCacheNodeForHydration]
    D --> E[Cache Seeding & Route Discovery]
    E --> F[Return AppRouterState]
```

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:32-100](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L32-L100)

The construction pipeline proceeds through a series of deterministic steps to establish the initial route tree and cache node structure:
1. `createInitialRouterState()` destructures `InitialRSCPayload` and normalizes the initial canonical URL and Flight data parts via `getFlightDataPartsFromPath`.
2. `convertRootFlightRouterStateToRouteTree()` converts the initial `FlightRouterState` into a `RouteTree`, tracking metadata vary paths.
3. `createInitialCacheNodeForHydration()` builds the initial cache node hierarchy using the route tree, seed data, head, and computed dynamic stale time.
4. `discoverKnownRoute()` is invoked if running in the browser with a valid metadata vary path, registering the route pattern for future navigation prediction.

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:38-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L38-L118)

> [!NOTE]
> For statically generated HTML pages, the `FlightRouterState` baked into the initial RSC payload may omit correct segment inlining hints. The server marks these trees with `InliningHintsStale`, causing the route cache entry to expire immediately so that subsequent prefetches fetch correct hints from the `/_tree` endpoint.

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:78-83](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L78-L83)

When running in the browser (`location !== null`), the initialization routine populates the segment cache depending on whether the page is partially or fully static:
- **Partially static pages:** If `initialStaticStageByteLength` and `initialFlightStreamForCache` are available, the Flight stream is cloned, truncated at the static stage byte boundary, decoded via `decodeStageUntilBoundary`, and cached using `writePrerenderResponseIntoCache` with `FetchStrategy.PPR`.
- **Fully static pages:** If seed data and stale times are present without a partial byte boundary, the entire decoded seed data is written directly into the cache via `writePrerenderResponseIntoCache`, and the unused stream is cancelled.
- **Runtime prefetch streams:** If `initialRuntimePrefetchStream` is present, `processRuntimePrefetchStream` decodes the stream and writes runtime data into the cache under `FetchStrategy.PPRRuntime`.

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:126-227](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L126-L227)

> [!WARNING]
> The initial hydration payload is treated as a complete, self-sufficient snapshot for rendering the page. The router deliberately avoids fetching missing data during initialization to preserve a reliable recovery path via full document reloads.

Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:230-244](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L230-L244)

## Navigation Reducer and Tree Transitions

Client navigation processing begins inside `navigateReducer`, which acts as the entry point for handling `NavigateAction` payloads from user interactions or programmatic navigation calls. The reducer performs early validation checks—intercepting external URLs and page redirect meta tags to trigger hard Multi-Page Application (MPA) navigations via `completeHardNavigation`—before delegating internal routing tasks to segment cache navigation handlers.

Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-41](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L41), [packages/next/src/client/components/segment-cache/navigation.ts:620-653](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L620-L653)

```mermaid
sequenceDiagram
    participant Action as NavigateAction
    participant Reducer as navigateReducer
    participant CacheNav as navigateUsingSegmentCache
    participant SoftNav as completeSoftNavigation

    Action->>Reducer: Dispatch navigation action
    Reducer->>Reducer: Check external URL / redirect meta
    alt External or Redirect
        Reducer->>CacheNav: completeHardNavigation()
    else Internal Navigation
        Reducer->>CacheNav: navigate(...)
        CacheNav->>SoftNav: completeSoftNavigation()
    end
```

Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L56), [packages/next/src/client/components/segment-cache/navigation.ts:600-618](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L600-L618)

Internal navigations processed through `navigateUsingSegmentCache` resolve prefetch trees, construct target route states, and coordinate Partial Prerendering (PPR) tasks. Once target cache nodes and `FlightRouterState` trees are computed, the navigation concludes by invoking completion routines.
- **Call Chain:** `navigateReducer()` → `navigateUsingSegmentCache()` (located in segment-cache navigation) → `completeSoftNavigation()` → constructs final `AppRouterState`.
- **Soft Navigation Completion:** `completeSoftNavigation` evaluates path changes for interception routes via `computeChangedPath`, detects hash-only URL modifications, computes scroll targets, and manages scroll reference invalidation across pending navigations.

Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L56), [packages/next/src/client/components/segment-cache/navigation.ts:600-618](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L600-L618), [packages/next/src/client/components/segment-cache/navigation.ts:655-791](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L655-L791)

> [!NOTE]
> During soft navigations, if a user opts out of scrolling (`scroll={false}`), any newly created per-node scroll reference is neutralized by setting `scrollRef.current = false`, while prior active scroll references carried forward on cache nodes remain intact.

Sources: [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725)

When building route trees for navigation, abstract route patterns are translated into concrete instances through `reifyRouteTree`, which substitutes dynamic segment values from resolved parameters and computes vary paths to key segment cache entries correctly.

Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-885](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L885)

```typescript
function reifyRouteTree(
  pattern: RouteTree,
  resolvedParams: ResolvedParams,
  search: NormalizedSearch,
  parentPartialVaryPath: PartialSegmentVaryPath | null,
  acc: ReifyAccumulator
): RouteTree
```

Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-885](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L885)

Parallel slots and page segments are traversed recursively. For page nodes, vary paths incorporate request keys and search parameters, whereas layout segments finalize without search parameters.

Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:924-982](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L924-L982)

| Navigation Behavior / Constant | Value / Type | Purpose in Navigation Reducer | Sources |
| :--- | :--- | :--- | :--- |
| `ScrollBehavior.NoScroll` | Enum value | Disables automatic scrolling for the current navigation action. | [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725) |
| `FreshnessPolicy.Default` | Enum value | Controls cache lookup freshness and fallback behavior during segment retrieval. | [packages/next/src/client/components/segment-cache/navigation.ts:606](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L606) |
| `DYNAMIC_STALETIME_MS` | Number (ms) | Dynamic segment staleness duration derived from experimental environment configurations. | [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:16-18](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L16-L18) |
| `STATIC_STALETIME_MS` | Number (ms) | Static segment staleness duration computed via segment cache settings. | [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:19-21](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L19-L21) |

Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:16-21](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L16-L21), [packages/next/src/client/components/segment-cache/navigation.ts:606](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L606), [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725)

> [!CAUTION]
> Javascript URLs (`javascript:`) passed to navigation actions are explicitly blocked and logged as security errors inside `completeHardNavigation`, immediately returning the unmodified router state.

Sources: [packages/next/src/client/components/segment-cache/navigation.ts:625-630](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L625-L630)

## Server Patch and Tree Reconciliation

When a route mismatch occurs or server-driven updates are received, the client router applies Flight router state patches to update active route subtrees and cache nodes. This reconciliation process is managed by `serverPatchReducer`, which validates whether the incoming server response matches the expected router state before executing a known route navigation with a refresh freshness policy.

Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:16-69](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L16-L69)

The server patch reconciliation workflow delegates execution through specific functions depending on whether payload validation succeeds. The execution sequence follows:
`serverPatchReducer()` → checks `action.mpa` / `action.seed` → validates `action.previousTree === state.tree` → `navigateToKnownRoute()` (or falls back to `completeHardNavigation()` or `refreshReducer()`).

Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:28-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L28-L68)

During tree traversal and reconciliation, utility functions inspect `FlightRouterState` structures to extract paths and parameters. The path extraction and tree differencing call-chain operates as:
`computeChangedPath()` → `computeChangedPathImpl()` → matches segments via `matchSegment()` → falls back to `extractPathFromFlightRouterState()` and `normalizeSegments()`.

Sources: [packages/next/src/client/components/router-reducer/compute-changed-path.ts:208-220](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L208-L220)

> [!NOTE]
> If a more recent navigation has occurred since the mismatched patch was dispatched (`action.previousTree !== state.tree`), `serverPatchReducer` aborts the retry and invokes `refreshReducer` to evict stale dynamic data while preserving the latest navigation state.

Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:35-40](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L35-L40)

The router state reducer handles several distinct action types governing server patches, refreshes, and navigation restoration, defined in the reducer type definitions.

Sources: [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6-L128)

| Action Constant / Interface | Type Value | Purpose in Router State Reducer | Sources |
| :--- | :--- | :--- | :--- |
| `ACTION_REFRESH` | `'refresh'` | Triggers a full page data refresh, fetching fresh Flight data and updating the root cache and router state. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:24-27](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L24-L27) |
| `ACTION_NAVIGATE` | `'navigate'` | Initiates client-side navigation (`push` or `replace`) using prefetched data or dynamic fetch requests. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:7](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L7), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:59-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L59-L87) |
| `ACTION_RESTORE` | `'restore'` | Applies a known router state from browser history states during `popstate` events. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:8](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L8), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:98-105](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L98-L105) |
| `ACTION_SERVER_PATCH` | `'server-patch'` | Applies provided Flight data and router tree patches back into the active client cache. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:9](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L9), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:117-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L117-L128) |
| `ACTION_HMR_REFRESH` | `'hmr-refresh'` | Handles hot-module reload refresh triggers within the router reducer. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L10), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:38-40](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L38-L40) |
| `ACTION_SERVER_ACTION` | `'server-action'` | Dispatches server action requests, parsing responses and handling revalidations and redirects. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:11](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L11), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:49-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L49-L56) |

Sources: [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6-L128)

## Refresh and HMR Reducers

Standard and hot-module reload (HMR) refreshes re-fetch dynamic RSC payload data for the current URL while coordinating segment cache invalidation. When a refresh action or an HMR update occurs, the reducer invalidates stale dynamic entries to ensure fresh content is displayed without throwing away the underlying route structure.

Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:23-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L23-L39), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10)

The refresh workflow processes standard refreshes and HMR refreshes through a dedicated sequence of helper functions. The call-chain executes as follows:
`refreshReducer()` / `hmrRefreshReducer()` → `refreshDynamicData()` → `invalidateBfCache()` → `hasInterceptionRouteInCurrentTree()` → `convertServerPatchToFullTree()` → `navigateToKnownRoute()`.

Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:19-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L19-L104), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10)

1. **Cache Invalidation Check:** `refreshReducer` checks whether testing flags bypass cache invalidation (`process.env.__NEXT_EXPOSE_TESTING_API && action.bypassCacheInvalidation`). If not bypassed, it invokes `invalidateSegmentCacheEntries(currentNextUrl, currentRouterState)`.
2. **Dynamic Data Refresh:** Both `refreshReducer` and `hmrRefreshReducer` delegate to `refreshDynamicData(state, freshnessPolicy)`, passing either `FreshnessPolicy.RefreshAll` or `FreshnessPolicy.HMRRefresh`.
3. **BFCache & Interception Resolution:** `refreshDynamicData` clears the back/forward cache via `invalidateBfCache()`, then resolves `nextUrlForRefresh` by evaluating `hasInterceptionRouteInCurrentTree(state.tree)`.
4. **Seed Conversion and Navigation:** A `refreshSeed` is generated by calling `convertServerPatchToFullTree()`, and the refresh is finalized by invoking `navigateToKnownRoute()` with a `'replace'` navigation type and `ScrollBehavior.NoScroll`.

Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:31-103](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L31-L103), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10)

> [!NOTE]
> During a refresh, the router invalidates the segment cache (which holds dynamic RSC data) but deliberately leaves the route cache intact, because the underlying route tree structure does not change across a refresh.

Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:23-27](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L23-L27)

| Function / Parameter | Type / Value | Purpose in Refresh Subsystem | Sources |
| :--- | :--- | :--- | :--- |
| `refreshReducer` | Function | Entry point for standard user or programmatic refreshes, invalidating segment cache and triggering dynamic data re-fetch. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:19-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L19-L39) |
| `hmrRefreshReducer` | Function | Entry point for Hot Module Replacement refreshes, invoking dynamic data re-fetch with an HMR policy. | [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10) |
| `FreshnessPolicy.RefreshAll` | Enum member | Policy value instructing navigation handlers to refresh all dynamic data. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:38-43](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L38-L43) |
| `FreshnessPolicy.HMRRefresh` | Enum member | Policy value indicating an HMR-triggered refresh across route segments. | [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-9](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L9) |
| `ScrollBehavior.NoScroll` | Enum member | Scroll preservation setting ensuring page scroll position remains unchanged during a refresh. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:63](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L63) |

Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:1-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L1-L104), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L1-L10)

## Server Action Execution and Revalidation

Server action execution and revalidation in the router reducer handles processing server action requests, parsing action Flight responses, resolving redirects, and updating cache state. When a server action is invoked, the action reducer extracts server reference info, encodes reply arguments, builds action headers including the router state tree, and executes the fetch request.

Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:104-121](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L104-L121)

The processing of a server action flows through a specific sequence of operations:
1. `fetchServerAction()` parses and encodes action arguments using `extractInfoFromServerReferenceId()`, `omitUnusedArgs()`, and `encodeReply()`.
2. The returned promise resolves in the reducer handler, checking `revalidationKind`. If revalidation occurs (`ActionDidRevalidateStaticAndDynamic`), `invalidateBfCache()` and `invalidateEntirePrefetchCache()` are invoked, followed by `startRevalidationCooldown()`.
3. If a redirect location is present, `isExternalURL()` determines whether to trigger an external MPA hard navigation via `completeHardNavigation()` or an internal SPA redirect with `createRedirectErrorForAction()`.
4. When new Flight data and rendered search results are returned (`flightData !== undefined && flightDataRenderedSearch !== undefined`), `convertServerPatchToFullTree()` generates a redirect seed, `discoverKnownRoute()` registers the route pattern, and `navigateToKnownRoute()` completes the transition.

Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:109-522](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L109-L522)

> [!WARNING]
> If a server action triggers a redirect without sending any Flight data, the router treats it as an external redirect and immediately forces a hard navigation via `completeHardNavigation()`.

Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:423-429](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L423-L429)

| Constant / Function | Type / Value | Purpose in Server Action Subsystem | Sources |
| :--- | :--- | :--- | :--- |
| `FetchServerActionResult` | Type Definition | Encapsulates redirect location, redirect type, revalidation kind, action result, flight data, search metadata, and interception flags. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:93-102](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L93-L102) |
| `ActionDidNotRevalidate` | Revalidation Kind | Indicates the server action performed no revalidation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:64](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L64) |
| `ActionDidRevalidateDynamicOnly` | Revalidation Kind | Indicates revalidation affected only dynamic segments. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:65](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L65) |
| `ActionDidRevalidateStaticAndDynamic` | Revalidation Kind | Indicates revalidation affected both static and dynamic cache entries, triggering entire prefetch cache invalidation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:66](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L66), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:361-363](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L361-L363) |
| `startRevalidationCooldown` | Function | Initiates a cooldown period before re-prefetching to allow CDN cache propagation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:51](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L51), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:367](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L367) |

Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:51-102](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L51-L102), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:343-368](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L343-L368)

## History Traversal and Restore Reducer

History traversal relies on the `restoreReducer` function to handle `popstate` events, reconstruct the target route state from history entries, and coordinate back/forward cache integration. When a user triggers browser navigation, the reducer inspects the incoming `RestoreAction` history state. If the history state lacks a valid `FlightRouterState`—such as for pre-hydration entries or anchor link hash navigations—it retains the existing tree via `state.tree` to prevent invalid router states. Otherwise, it extracts the restore tree, rendered search parameters, and canonical URL.

Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:22-42](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L22-L42)

The restoration process follows an explicit execution sequence from state extraction through task spawning and tree traversal:
1. `restoreReducer()` — Receives `state` and `RestoreAction`, resolving `treeToRestore` from `historyState.tree` or falling back to `state.tree`.
2. `convertServerPatchToFullTree()` — Takes the restored tree, current timestamp, and unknown dynamic stale times (`UnknownDynamicStaleTime`) to build a full `NavigationSeed` containing the route tree and vary paths.
3. `startPPRNavigation()` — Evaluates the navigation task using `FreshnessPolicy.HistoryTraversal`, evaluating the segment cache against the restore seed route tree.
4. Branch check (`task === null`) — If the task creation fails, it falls back to a hard navigation via `completeHardNavigation(state, restoredUrl, 'replace')`. Otherwise, it proceeds to spawn dynamic requests.
5. `spawnDynamicRequests()` — Dispatches background requests for dynamic data using the `'replace'` navigate type and history traversal freshness policy.
6. `completeTraverseNavigation()` — Finalizes the traversal update, returning a new `AppRouterState` with `preserveCustomHistoryState` set to `true`.

Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:22-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L22-L104), [packages/next/src/client/components/segment-cache/navigation.ts:803-822](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L803-L822)

> [!WARNING]
> History traversal never uses route prediction. If a dynamic data mismatch occurs during a restore task, the retry handler must traverse the known route tree to locate and mark the mismatched entry.

Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:82-92](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L82-L92)

When restoring state, helper utilities inspect router trees to determine pathnames and parameters. `extractPathFromFlightRouterState()` processes segment nodes, ignoring default segment keys and interception markers, while `computeChangedPath()` calculates differences between state trees during traversals.

Sources: [packages/next/src/client/components/router-reducer/compute-changed-path.ts:81-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L81-L118), [packages/next/src/client/components/router-reducer/compute-changed-path.ts:208-220](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L208-L220)

## Related

- [Client App Router](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/client-app-router)
- [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.
