---
title: "App Router"
description: "The App Router coordinates client-side navigation, React Server Component (RSC) tree reconciliation, and route segment caching in Next.js applications. It processes client actions sequentially, han..."
last_updated: "2026-10-12T06:38:56.86852+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-runtime/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/layout-router.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx)
- [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router.tsx)
- [packages/next/src/server/app-render/create-component-tree.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/create-component-tree.tsx)
- [packages/next/src/client/index.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/index.tsx)
- [packages/next/src/client/components/render-tree.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/render-tree.ts)
- [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx)
- [packages/next/src/build/webpack/loaders/next-app-loader/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/next-app-loader/index.ts)
- [packages/next/src/client/components/app-router-state.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-state.ts)
- [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts)
- [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx)
- [packages/next/src/server/render.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/render.tsx)
- [packages/next/src/client/components/sequential-router-queue.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/sequential-router-queue.ts)
- [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/navigation.ts)
- [packages/next/src/shared/lib/app-router-context.shared-runtime.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts)
- [packages/next/src/client/components/links.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/links.ts)
- [packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts)
- [packages/next/src/client/components/app-router-announcer.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-announcer.tsx)
- [packages/next/src/client/link.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx)
- [packages/next/src/client/resolve-href.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts)
- [packages/next/src/shared/lib/router/utils/interception-routes.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/interception-routes.ts)
- [packages/next/src/shared/lib/router/utils/is-dynamic.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-dynamic.ts)
- [packages/next/src/shared/lib/router/utils/is-local-url.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-local-url.ts)
- [packages/next/src/shared/lib/utils.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts)
</details>

## Overview

The App Router coordinates client-side navigation, React Server Component (RSC) tree reconciliation, and route segment caching in Next.js applications. It processes client actions sequentially, handles link prefetching via intersection observation, and manages hierarchical layout nesting, error boundaries, and scroll restoration across route transitions.

Sources: [packages/next/src/client/components/layout-router.tsx:33-40](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L33-L40), [packages/next/src/client/components/app-router.tsx:27-50](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router.tsx#L27-L50), [packages/next/src/client/components/app-router-state.ts:54-121](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-state.ts#L54-L121), [packages/next/src/client/components/links.ts:110-127](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/links.ts#L110-L127)

## Client initialization and root provider hierarchy

### Context Architecture and Root Providers

During browser bootstrapping, the client runtime consumes the initial Flight response and constructs the foundational component tree [Client Runtime](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-runtime/client-runtime). The root hierarchy configures strict mode, head management, root layout boundaries, and screen reader route announcements through specialized context providers and portals.

```mermaid
flowchart TD
    StrictMode["StrictModeIfEnabled"] --> HeadManager["HeadManagerContext.Provider"]
    HeadManager --> Root["Root"]
    Root --> ServerRoot["ServerRoot"]
    ServerRoot --> MissingSlot["MissingSlotContext (Dev Only)"]
    MissingSlot --> AppRouter["AppRouter"]
    AppRouter --> Announcer["AppRouterAnnouncer"]
```

Sources: [packages/next/src/client/app-index.tsx:388-401](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx#L388-L401), [packages/next/src/client/app-index.tsx:280-311](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx#L280-L311)

The root provider hierarchy established in `hydrate` wraps the application in several nested contexts before mounting the [App Rendering](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-rendering/app-rendering) tree:

| Provider / Wrapper | Source File | Purpose |
| :--- | :--- | :--- |
| `StrictModeIfEnabled` | `packages/next/src/client/app-index.tsx` | Conditionally enables `React.StrictMode` based on the `__NEXT_STRICT_MODE_APP` environment flag, falling back to `React.Fragment`. |
| `HeadManagerContext.Provider` | `packages/next/src/client/app-index.tsx` | Configures document head management with `appDir: true`. |
| `Root` | `packages/next/src/client/app-index.tsx` | Handles test mode hooks (`window.__NEXT_HYDRATED`) when `__NEXT_TEST_MODE` is active. |
| `MissingSlotContext` | `packages/next/src/shared/lib/app-router-context.shared-runtime.ts` | Provides missing parallel route slot identifiers during development when `initialRSCPayload.m` is present. |

Sources: [packages/next/src/client/app-index.tsx:313-328](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx#L313-L328), [packages/next/src/client/app-index.tsx:388-401](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx#L388-L401), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts:124-124](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L124-L124)

> [!NOTE]
> When `document.documentElement.id` equals `__next_error__`, server rendering has failed. In development, Next.js wraps the root element in `RootLevelDevOverlayElement` and invokes `ReactDOMClient.createRoot` directly without hydration to avoid mismatch errors.
> Sources: [packages/next/src/client/app-index.tsx:403-416](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/app-index.tsx#L403-L416)

### Accessibility Announcer and Shadow DOM

Screen reader route change announcements are managed by `AppRouterAnnouncer`. It creates or retrieves a custom element named `next-route-announcer` containing a shadow DOM with an assertive `aria-live` region (`__next-route-announcer__`). 

```tsx
function getAnnouncerNode() {
  const existingAnnouncer = document.getElementsByName(ANNOUNCER_TYPE)[0]
  if (existingAnnouncer?.shadowRoot?.childNodes[0]) {
    return existingAnnouncer.shadowRoot.childNodes[0] as HTMLElement
  } else {
    const container = document.createElement(ANNOUNCER_TYPE)
    container.style.cssText = 'position:absolute'
    const announcer = document.createElement('div')
    announcer.ariaLive = 'assertive'
    announcer.id = ANNOUNCER_ID
    announcer.role = 'alert'
    announcer.style.cssText =
      'position:absolute;border:0;height:1px;margin:-1px;padding:0;width:1px;clip:rect(0 0 0 0);overflow:hidden;white-space:nowrap;word-wrap:normal'

    // Use shadow DOM here to avoid any potential CSS bleed
    const shadow = container.attachShadow({ mode: 'open' })
    shadow.appendChild(announcer)
    document.body.appendChild(container)
    return announcer
  }
}
```
Sources: [packages/next/src/client/components/app-router-announcer.tsx:8-28](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-announcer.tsx#L8-L28)

Upon route tree updates, the announcer evaluates `document.title` or falls back to the inner text of the first `<h1>` element. If the title changes after initial load, the new title string is ported into the shadow DOM container to notify assistive technologies.

```tsx
useEffect(() => {
  let currentTitle = ''
  if (document.title) {
    currentTitle = document.title
  } else {
    const pageHeader = document.querySelector('h1')
    if (pageHeader) {
      currentTitle = pageHeader.innerText || pageHeader.textContent || ''
    }
  }

  // Only announce the title change, but not for the first load because screen
  // readers do that automatically.
  if (
    previousTitle.current !== undefined &&
    previousTitle.current !== currentTitle
  ) {
    setRouteAnnouncement(currentTitle)
  }
  previousTitle.current = currentTitle
}, [tree])
```
Sources: [packages/next/src/client/components/app-router-announcer.tsx:47-67](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-announcer.tsx#L47-L67)

## Router action queue and navigation lifecycle

### Sequential Action Execution and Transition Queuing

Navigation execution and state mutations in the App Router are coordinated through an action queue managed by `AppRouterActionQueue` nodes. When actions such as navigations, restores, or refreshes are dispatched via `dispatchAction`, they are either executed immediately if the queue is idle or appended to a linked list of `ActionQueueNode` items.

```mermaid
flowchart TD
    Dispatch["dispatchAction()"] --> QueueEmpty{"actionQueue.pending === null?"}
    QueueEmpty -- Yes --> RunImmediate["runAction() at head"]
    QueueEmpty -- No --> IsNav{"payload.type is ACTION_NAVIGATE or ACTION_RESTORE?"}
    IsNav -- Yes --> Discard["Mark pending as discarded\nSet wasPreempted = true"]
    Discard --> RunNav["Run navigation action immediately"]
    IsNav -- No --> Append["Append to actionQueue.last"]
```

Sources: [packages/next/src/client/components/app-router-instance.ts:30-39](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L30-L39), [packages/next/src/client/components/app-router-instance.ts:152-227](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L152-L227)

Navigations and back/forward traversals (`ACTION_NAVIGATE` and `ACTION_RESTORE`) hold high priority in the queue. When a navigation is dispatched while another action is pending, the current pending node is marked as `discarded = true` and `actionQueue.wasPreempted` is set to true so that intermediate states do not trigger unnecessary React renders.

| Action Type | Payload Structure | Execution Behavior | Source File |
| :--- | :--- | :--- | :--- |
| `ACTION_NAVIGATE` | `url`, `isExternalUrl`, `locationSearch`, `scrollBehavior`, `navigateType` | Immediately preempts pending actions, marks pending nodes discarded, and kicks off sequential router transitions. | `packages/next/src/client/components/sequential-router-queue.ts` |
| `ACTION_RESTORE` | `url`, `historyState` | Executes rapidly without creating deferred promises to prevent input latency during browser history popstate events. | `packages/next/src/client/components/sequential-router-queue.ts` |
| `ACTION_REFRESH` | *None* | Flushes revalidated data when the queue returns to an idle state after discarded [server actions](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-rendering/server-actions). | `packages/next/src/client/components/app-router-instance.ts` |
| `ACTION_HMR_REFRESH` | `signal` | Aborts superseded requests using an `AbortController` generation tracker in development mode. | `packages/next/src/client/components/sequential-router-queue.ts` |

Sources: [packages/next/src/client/components/app-router-instance.ts:162-177](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L162-L177), [packages/next/src/client/components/sequential-router-queue.ts:17-25](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/sequential-router-queue.ts#L17-L25), [packages/next/src/client/components/sequential-router-queue.ts:165-190](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/sequential-router-queue.ts#L165-L190)

> [!NOTE]
> `ACTION_RESTORE` actions bypass the creation of a deferred promise in `dispatchAction`. Because `popstate` events fire on browser back/forward traversals, avoiding asynchronous Promise wrappers ensures zero artificial delay on history navigations.
> Sources: [packages/next/src/client/components/app-router-instance.ts:162-166](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L162-L166)

### External URL Detection and Navigation Runners

The sequential router queue enforces security guards before triggering navigations. If a target href resolves to a `javascript:` URL string, the action runner throws an immediate security error.

```typescript
if (isJavaScriptURLString(href)) {
  throw new Error(
    'Next.js has blocked a javascript: URL as a security precaution.'
  )
}
```
Sources: [packages/next/src/client/components/sequential-router-queue.ts:55-59](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/sequential-router-queue.ts#L55-L59)

When a navigation is executed through `navigate`, transition wrappers add active transition types via `React.addTransitionType` and invoke `startRouterTransition` alongside dispatching the application router action with explicit external URL flags derived from `isExternalURL(url)`.

```typescript
  if (transitionTypes) {
    for (const type of transitionTypes) {
      addTransitionType(type)
    }
  }

  const url = new URL(addBasePath(href), location.href)
  if (process.env.__NEXT_APP_NAV_FAIL_HANDLING) {
    window.next.__pendingUrl = url
  }

  setLinkForCurrentNavigation(linkInstanceRef)
  startRouterTransition(
    href,
    navigateType,
    getRequiredAppRouterState().tree,
    prefetchIntent
  )

  dispatchAppRouterAction({
    type: ACTION_NAVIGATE,
    url,
    isExternalUrl: isExternalURL(url),
    locationSearch: location.search,
    scrollBehavior,
    navigateType,
  })
})
```
Sources: [packages/next/src/client/components/sequential-router-queue.ts:65-92](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/sequential-router-queue.ts#L65-L92)

> [!CAUTION]
> Discarded server actions that indicate data revalidation (`didRevalidate: true`) cause the action queue to flag `actionQueue.needsRefresh = true`. Once the queue drains completely, an automatic `ACTION_REFRESH` is dispatched to ensure server data consistency.
> Sources: [packages/next/src/client/components/app-router-instance.ts:114-123](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L114-L123), [packages/next/src/client/components/app-router-instance.ts:83-88](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-instance.ts#L83-L88)

## Client link resolution and destination checking

### Link Resolution and Destination Check Execution

When an application renders a `<Link>` component, target URLs undergo a deterministic path normalization and resolution sequence before they can be evaluated as local routes or interception targets. The verified call chain for this check flows as follows: `Link` → `resolveHref` → `isLocalURL` → `getLocationOrigin`.

1. `Link` — The React component instance receives `href` and `as` props, memoizing their resolved string outputs via `resolveHref` when a router context is available.
Sources: [packages/next/src/client/link.tsx:476-491](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L476-L491)
2. `resolveHref` — Formats string or object URLs, inspects for repeated slashes or backslashes, and passes the normalized string to `isLocalURL`.
Sources: [packages/next/src/client/resolve-href.ts:35-58](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L35-L58)
3. `isLocalURL` — Determines whether the URL is routable by checking absolute scheme rules via `isAbsoluteUrl` and cross-origin matching via `getLocationOrigin`.
Sources: [packages/next/src/shared/lib/router/utils/is-local-url.ts:7-17](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-local-url.ts#L7-L17)
4. `getLocationOrigin` — Extracts `protocol`, `hostname`, and `port` from `window.location` to construct the baseline origin string for local comparison.
Sources: [packages/next/src/shared/lib/utils.ts:338-341](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts#L338-L341)

```mermaid
sequenceDiagram
    participant Link as packages/next/src/client/link.tsx
    participant ResolveHref as packages/next/src/client/resolve-href.ts
    participant IsLocalURL as packages/next/src/shared/lib/router/utils/is-local-url.ts
    participant Utils as packages/next/src/shared/lib/utils.ts

    Link->>ResolveHref: resolveHref(router, hrefProp, true)
    ResolveHref->>IsLocalURL: isLocalURL(urlAsString)
    IsLocalURL->>Utils: isAbsoluteUrl(url)
    IsLocalURL->>Utils: getLocationOrigin()
```
Sources: [packages/next/src/client/link.tsx:485-485](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L485-L485), [packages/next/src/client/resolve-href.ts:56-56](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L56-L56), [packages/next/src/shared/lib/router/utils/is-local-url.ts:9-12](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-local-url.ts#L9-L12), [packages/next/src/shared/lib/utils.ts:338-341](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts#L338-L341)

### Absolute URL and Dynamic Route Interception Traces

Destination checking extends to validating absolute scheme headers and uncovering dynamic route segments that participate in route interception:

1. `Link` — Invokes `resolveHref` to parse the target href for path interpolation.
Sources: [packages/next/src/client/link.tsx:485-485](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L485-L485)
2. `resolveHref` — Evaluates dynamic routes using `isDynamicRoute` and matches route parameters.
Sources: [packages/next/src/client/resolve-href.ts:70-74](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L70-L74)
3. `isLocalURL` — Calls `isAbsoluteUrl` to execute a fast character code check against `ABSOLUTE_URL_REGEX`.
Sources: [packages/next/src/shared/lib/router/utils/is-local-url.ts:9-9](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-local-url.ts#L9-L9), [packages/next/src/shared/lib/utils.ts:327-336](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts#L327-L336)
4. `isAbsoluteUrl` — Inspects whether the initial character code represents a letter and tests against `/^[a-zA-Z][a-zA-Z\d+\-.]*?:/`.
Sources: [packages/next/src/shared/lib/utils.ts:327-336](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts#L327-L336)

```mermaid
sequenceDiagram
    participant Link as packages/next/src/client/link.tsx
    participant ResolveHref as packages/next/src/client/resolve-href.ts
    participant IsLocalURL as packages/next/src/shared/lib/router/utils/is-local-url.ts
    participant Utils as packages/next/src/shared/lib/utils.ts

    Link->>ResolveHref: resolveHref(router, hrefProp, true)
    ResolveHref->>ResolveHref: isDynamicRoute(finalUrl.pathname)
    ResolveHref->>IsLocalURL: isLocalURL(urlAsString)
    IsLocalURL->>Utils: isAbsoluteUrl(url)
```
Sources: [packages/next/src/client/link.tsx:485-485](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L485-L485), [packages/next/src/client/resolve-href.ts:105-105](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L105-L105), [packages/next/src/client/resolve-href.ts:56-56](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L56-L56), [packages/next/src/shared/lib/router/utils/is-local-url.ts:9-9](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-local-url.ts#L9-L9), [packages/next/src/shared/lib/utils.ts:327-336](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/utils.ts#L327-L336)

Alternatively, dynamic route checks evaluate interception pathways:

1. `Link` — Resolves the destination href via `resolveHref`.
Sources: [packages/next/src/client/link.tsx:485-485](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L485-L485)
2. `resolveHref` — Invokes `isDynamicRoute` on path candidates.
Sources: [packages/next/src/client/resolve-href.ts:70-70](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L70-L70)
3. `isDynamicRoute` — Inspects whether a route is dynamic and delegates to `isInterceptionRouteAppPath` if interception markers are detected.
Sources: [packages/next/src/shared/lib/router/utils/is-dynamic.ts:19-22](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-dynamic.ts#L19-L22)
4. `isInterceptionRouteAppPath` — Splits the path by `/` and checks segment matching against `INTERCEPTION_ROUTE_MARKERS`.
Sources: [packages/next/src/shared/lib/router/utils/interception-routes.ts:119-128](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/interception-routes.ts#L119-L128)

```mermaid
sequenceDiagram
    participant Link as packages/next/src/client/link.tsx
    participant ResolveHref as packages/next/src/client/resolve-href.ts
    participant IsDynamic as packages/next/src/shared/lib/router/utils/is-dynamic.ts
    participant Interception as packages/next/src/shared/lib/router/utils/interception-routes.ts

    Link->>ResolveHref: resolveHref(router, hrefProp, true)
    ResolveHref->>IsDynamic: isDynamicRoute(router.pathname)
    IsDynamic->>Interception: isInterceptionRouteAppPath(route)
```
Sources: [packages/next/src/client/link.tsx:485-485](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/link.tsx#L485-L485), [packages/next/src/client/resolve-href.ts:70-70](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L70-L70), [packages/next/src/shared/lib/router/utils/is-dynamic.ts:20-22](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/is-dynamic.ts#L20-L22), [packages/next/src/shared/lib/router/utils/interception-routes.ts:119-128](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/interception-routes.ts#L119-L128)

### Interception Markers and Route Utilities

Route interception relies on specific prefix markers defined in router utilities to intercept navigation targets across directory levels.

| Interception Marker | Behavior | Source File |
| :--- | :--- | :--- |
| `(..)(..)` | Matches route segments two levels up from the intercepting route location. | `packages/next/src/shared/lib/router/utils/interception-routes.ts:4-9` |
| `(.)` | Matches sibling route segments at the same directory level as the intercepting route. | `packages/next/src/shared/lib/router/utils/interception-routes.ts:4-9` |
| `(..)` | Matches route segments one level up from the intercepting route location. | `packages/next/src/shared/lib/router/utils/interception-routes.ts:4-9` |
| `(...)` | Matches route segments located in the root directory. | `packages/next/src/shared/lib/router/utils/interception-routes.ts:4-9` |

Sources: [packages/next/src/shared/lib/router/utils/interception-routes.ts:4-9](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/interception-routes.ts#L4-L9)

> [!WARNING]
> Using the `(..)` interception marker at the root level (`/`) throws an explicit runtime error because root segments cannot ascend further. Developers must use the `(.)` marker for root-level sibling interceptions.
> Sources: [packages/next/src/shared/lib/router/utils/interception-routes.ts:179-183](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/router/utils/interception-routes.ts#L179-L183)

> [!CAUTION]
> Repeated forward slashes (`//`) or backslashes (`\`) inside an `href` string passed to `resolveHref` trigger a console error and are automatically normalized using `normalizeRepeatedSlashes` to prevent malformed route matching.
> Sources: [packages/next/src/client/resolve-href.ts:47-53](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/resolve-href.ts#L47-L53)

## Visible link observation and prefetch coordination

Link instances coordinate with an `IntersectionObserver` instance initialized with a root margin of `200px` to track visibility and trigger prefetch execution pipelines when links enter the viewport.
Sources: [packages/next/src/client/components/links.ts:121-127](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/links.ts#L121-L127)

DOM elements are mapped to prefetchable instances using a `WeakMap` where available, or a standard `Map` fallback, while currently visible instances are maintained within a separate `Set` for efficient enumeration during cache invalidations or route changes.
Sources: [packages/next/src/client/components/links.ts:110-119](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/links.ts#L110-L119)

```typescript
export interface PrefetchOptions {
  kind: PrefetchKind
  onInvalidate?: () => void
}
```
Sources: [packages/next/src/shared/lib/app-router-context.shared-runtime.ts:30-33](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L30-L33)

> [!NOTE]
> `mountLinkInstance` verifies whether prefetching is enabled and coerces destination paths into valid `URL` objects via `coercePrefetchableUrl`, reporting failures through `reportError` or `console.error` if string conversion fails.
> Sources: [packages/next/src/client/components/links.ts:144-182](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/links.ts#L144-L182)

## Render tree construction and segment reconciliation

### Navigating Prefetched Routes and Root Layouts

Client-side navigation checks the [segment cache](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-runtime/segment-cache) for matching fulfilled route entries, invoking `navigateUsingPrefetchedRouteTree` when an entry is found to construct the prefetch seed and initiate `navigateToKnownRoute`.
Sources: [packages/next/src/client/components/app-router-state.ts:143-161](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-state.ts#L143-L161), [packages/next/src/client/components/app-router-state.ts:389-433](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/app-router-state.ts#L389-L433)

Navigations cross root layout boundaries when the highest-level layout changes between trees, requiring full-page multi-page application navigations. The `isNavigatingToNewRootLayout` function evaluates this condition by checking the `PrefetchHint.IsRootLayoutOrAbove` bit flag on both `currentTree` and `nextTree`.
Sources: [packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts:1-26](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts#L1-L26)

> [!NOTE]
> `isNavigatingToNewRootLayout` compares segment names and types while ignoring dynamic parameter values, allowing slugs like `slug1` and `slug2` under `/[name]/layout.js` to share a root layout unless group identifiers or structure change.
> Sources: [packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts:35-51](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts#L35-L51)

### Reusing Inactive Segments and Creating Dynamic Requests

When soft navigations encounter reused default slots from older routes, `reuseActiveSegmentInDefaultSlot` rebases the inactive route tree and preserves the refresh context containing the canonical URL and rendered search state.
Sources: [packages/next/src/client/components/render-tree.ts:939-971](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/render-tree.ts#L939-L971)

The `createDynamicRequestTree` function constructs a `FlightRouterState` object instructing the server how to render requested segments, or returns `null` to skip requests when neither the segment nor its children require new data.
Sources: [packages/next/src/client/components/render-tree.ts:880-914](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/render-tree.ts#L880-L914)

> [!CAUTION]
> The `'refetch'` marker is explicitly assigned to index `3` of the top-most dynamic request tree node that requires new data, but is omitted if a parent segment was already marked.
> Sources: [packages/next/src/client/components/render-tree.ts:898-902](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/render-tree.ts#L898-L902)

## Layout boundary coordination and scroll handling

### Hierarchical Layout Nesting and Error Boundaries

The client runtime manages hierarchical route structures through nested `LayoutRouterContext` and `GlobalLayoutRouterContext` providers. Each segment level accumulates route parameters and wrap segments within specialized runtime boundaries such as `ErrorBoundary`, `RedirectBoundary`, `HTTPAccessFallbackBoundary`, and [instant validation](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-rendering/instant-validation) components.
Sources: [packages/next/src/client/components/layout-router.tsx:33-40](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L33-L40), [packages/next/src/client/components/layout-router.tsx:632-647](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L632-L647), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts:97-113](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L97-L113)

```markdown
| Context Provider | Value Structure | Purpose |
| :--- | :--- | :--- |
| `LayoutRouterContext` | `{ parentTree, parentRenderTree, parentSegmentPath, parentParams, parentLoadingData, debugNameContext, url, isActive }` | Provides current segment tree metadata, params, and active rendering state to nested child routers. |
| `GlobalLayoutRouterContext` | `{ tree, scrollRef, nextUrl, previousNextUrl }` | Exposes global navigation state and scroll handling references across layout trees. |
| `TemplateContext` | `React.ReactNode` | Wraps template leaf slots around page contents. |
| `AppRouterContext` | `AppRouterInstance | null` | Exposes imperative navigation methods (`push`, `replace`, `refresh`, `back`, `forward`, `prefetch`, `bfcacheId`) to components. |
```
Sources: [packages/next/src/client/components/layout-router.tsx:28-31](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L28-L31), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts:35-96](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L35-L96), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts:97-122](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L97-L122)

> [!NOTE]
> `useRouter()` reads the `bfcacheId` numeric property from the closest parent render tree data node and formats it with a `_b_` prefix and trailing underscores (e.g., `_b_0_`) to generate a unique string identifier for React keys.
> Sources: [packages/next/src/client/components/navigation.ts:179-192](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/navigation.ts#L179-L192)

### Scroll Positioning and Viewport Target State

Scroll management is driven by `InnerScrollHandler` alongside helper utilities that evaluate target viewports and DOM nodes for hash fragments or element positions during route transitions.
Sources: [packages/next/src/client/components/layout-router.tsx:55-137](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L55-L137), [packages/next/src/client/components/layout-router.tsx:148-160](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L148-L160)

The `ScrollTargetState` enum defines the viewport positioning classification returned during layout coordinate calculations.

```markdown
| Enum Member | Meaning |
| :--- | :--- |
| `ScrollTargetState.NoClientRects` | The target instance returns zero client rectangles via `getClientRects()`. |
| `ScrollTargetState.InViewport` | The element top coordinate falls within the resolved scroll padding and viewport height. |
| `ScrollTargetState.OutOfViewport` | The element top coordinate lies outside the usable viewport bounds. |
```
Sources: [packages/next/src/client/components/layout-router.tsx:55-59](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L55-L59), [packages/next/src/client/components/layout-router.tsx:97-116](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L97-L116)

> [!WARNING]
> When a hash fragment equals `'top'`, `getHashFragmentDomNode` resolves directly to `document.body`; otherwise, it queries elements via `document.getElementById` or falls back to `document.getElementsByName`.
> Sources: [packages/next/src/client/components/layout-router.tsx:124-136](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/layout-router.tsx#L124-L136)

## Related

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


## Sitemap

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