vercel / vercel/next.js

App Router: root route's server-side usePathname() is "/index" on Vercel, causing hydration mismatch (#418)

Open
#95,648 6 comments 0 reactions 1 assignee View on GitHub

@icyJoseph is already working on this.

Since Jul 10, 2026.

Runtime
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/cyrilpsaju/nextjs-root-pathname-repro

To Reproduce
  1. Clone the reproduction above. It is a minimal App Router app: a root layout, a route group, and a client component in the group layout that renders only on the root route:
// app/(group)/chrome.js
"use client"
import { usePathname } from "next/navigation"

export function Chrome() {
  const pathname = usePathname()
  if (pathname !== "/") return null
  return <header id="chrome">root-only chrome</header>
}
  1. Local control — behaves correctly:
npm install && npm run build && npm start
curl -s localhost:3000/    | grep -c 'id="chrome"'                 # 1
curl -s localhost:3000/    | grep -o '\\"c\\":\[[^]]*\]' | head -1  # "c":["",""]
curl -s localhost:3000/faq | grep -c 'id="chrome"'                 # 0
  1. Deploy the same commit to Vercel, then:
curl -s https://<deployment-host>/ | grep -c 'id="chrome"'                 # 0  <-- expected 1
curl -s https://<deployment-host>/ | grep -o '\\"c\\":\[[^]]*\]' | head -1  # "c":["","index"]  <-- expected ["",""]
curl -s https://<deployment-host>/faq | grep -o '\\"c\\":\[[^]]*\]' | head -1  # "c":["","faq"]   correct
  1. Load the deployed root route in a browser: React logs hydration error #418, once per load.
Current vs. Expected behavior

Current (deployed to Vercel): for the root route only, the server-side canonical pathname is /index. usePathname() therefore returns /index during SSR and / on the client, so a client component branching on pathname === "/" renders a different tree on each side. React reports hydration error #418, discards the server HTML for that subtree and re-renders it on the client. The subtree is absent from the server-rendered HTML.

Expected: usePathname() returns / on the server for the root route, matching the client — and matching the x-matched-path: / the response already reports.

Evidence — the RSC Flight payload. The Flight payload embedded in the SSR HTML carries the router's canonical path segments as "c":

route deployed to Vercel local next build && next start
/ "c":["","index"] "c":["",""]
/faq "c":["","faq"] "c":["","faq"]

Additional observations:

  • The response reports x-matched-path: /, so routing matched the root route. Only the canonical serialised into the Flight payload is index — which is the prerender's filename, .next/server/app/index.html.
  • The client component's reference is present in the deployed Flight payload, so the server was asked to render it and it returned null. It is not dropped downstream by streaming or caching.
  • next build locally produces .next/server/app/index.html that does contain the component's output.
  • next start serves both / and /index with "c":["",""] and renders the component, so Next's own server normalises the root route.
  • Deterministic: exactly one #418 per load, on fast and throttled networks, and across x-vercel-cache: HIT and STALE alike.

Impact. Beyond the console error, the affected subtree is missing from the SSR HTML. In a real application this meant a site header and all of its internal navigation links were absent from the server-rendered output — invisible to crawlers that do not execute JavaScript (confirmed with a Googlebot user agent) — and a bottom navigation bar server-rendered with no active tab. Measured: no layout shift (CLS 0.000), no visible flicker; the cost is the lost SSR output and the re-render.

Temporary mitigation (not a proposed fix). Accepting both spellings restores matching output:

const isRoot = pathname === "/" || pathname === "/index"

Safe only because /index is not a routable page (it normalises to the root route). It is a compatibility shim — the underlying canonical is still wrong. Noted in case others hit this first.

Provide environment information
Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11
Binaries:
  Node: 24.14.1
  npm: 11.11.0
Relevant Packages:
  next: 16.2.9
  react: 19.2.4
  react-dom: 19.2.4

Deployed on Vercel. Reproduced on two independent Vercel projects built from the same commit, in an application with the structure above.

Which area(s) are affected? (Select all that apply)

Navigation, Runtime, Vercel

Which stage(s) are affected? (Select all that apply)

next build (local), Vercel (Deployed)

Additional context

I verified the linked reproduction locally: it renders the component and reports "c":["",""], as expected. I was unable to publish a public Vercel deployment of it — Deployment Protection is enforced on my account, so the deployment URL answers 302 to the Vercel SSO endpoint. Deploying the repo should surface the divergence; the curl checks in To Reproduce are the ones to run.

The deployed-vs-local divergence itself was observed on two Vercel production deployments of an application with this exact structure.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.