App Router: root route's server-side usePathname() is "/index" on Vercel, causing hydration mismatch (#418)
@icyJoseph is already working on this.
Since Jul 10, 2026.
- 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
- 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>
}
- 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
- 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
- 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 isindex— 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 buildlocally produces.next/server/app/index.htmlthat does contain the component's output.next startserves both/and/indexwith"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: HITandSTALEalike.
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.