nextjs - 💡(How to fix) Fix Parallel-route slot retains stale rendered output across soft-navigation in Next 16 + Turbopack

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The third attempt is the strongest signal that the bug is at the router-cache / RSC-payload layer rather than slot file resolution — default.tsx apparently doesn't re-execute for the new URL because the previously-rendered RSC payload for the slot is reused.

Fix Action

Fix / Workaround

Workarounds attempted (all fail)

Code Example

"use client"
import { usePathname } from "next/navigation"

export function ClientBreadcrumb() {
  const pathname = usePathname()
  return <div>client: {pathname}</div>
}

---

Operating System:
  Platform: darwin
  Arch: arm64
Binaries:
  Node: 22.22.0
  pnpm: 10.31.0
Relevant Packages:
  next: 16.2.4
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Also reproduced against `[email protected]` + `[email protected]` (verified 2026-06-05) in a throwaway copy of the same repo.
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/yp-commits/nextjs-parallel-slot-repro

To Reproduce

Clone the repo at https://github.com/yp-commits/nextjs-parallel-slot-repro, run pnpm install && pnpm dev, open http://localhost:3000.

  1. The header reads default: / — correct (no explicit slot for /, falls through to @breadcrumb/default.tsx).
  2. Click Artist: foo in the sidebar. URL becomes /roster/foo. Header reads explicit: roster/foo — correct (the @breadcrumb/roster/[slug]/page.tsx matched).
  3. Click Dashboard in the sidebar. URL becomes /. Sidebar updates. The page's main content swaps to the dashboard route.
  4. The header still reads explicit: roster/foo. The slot did not fall through to default.tsx for the new URL.
  5. Hard refresh (Cmd+Shift+R / Ctrl+Shift+R) → header now reads default: /.

Also reproduces on [email protected] + [email protected] (verified 2026-06-05). Reproduced in both next dev and next build + next start (Turbopack). Originally observed on a production Vercel deploy.

Current vs. Expected behavior

Expected

After step 3, the URL is /. The @breadcrumb slot has no matching explicit page.tsx for /, so it should fall through to app/(dashboard)/@breadcrumb/default.tsx, which reads the live x-pathname and renders default: /.

This is what the default.js docs describe:

default.js is used as the fallback for the soft navigation behavior of unmatched slots.

Actual

The slot retains the previously-rendered explicit page output (explicit: roster/foo). The fallback default.tsx is not invoked for the new URL on soft-navigation. The Server Component never re-renders.

Workarounds attempted (all fail)

Three documented invalidation patterns were tested on the same project before stripping the slot:

  1. Per-segment default.tsx — added @breadcrumb/roster/default.tsx re-exporting the root default. Recommended for "swapping slots on navigation OUT of the segment". ❌
  2. Sibling default.tsx next to each explicit page.tsx — added @breadcrumb/roster/[slug]/default.tsx. ❌
  3. Structural collapse — deleted every explicit page.tsx inside the slot, leaving only the root default.tsx. With no explicit page to retain, the slot should always fall through to default.tsx. ❌ Still stale on soft-nav.

The third attempt is the strongest signal that the bug is at the router-cache / RSC-payload layer rather than slot file resolution — default.tsx apparently doesn't re-execute for the new URL because the previously-rendered RSC payload for the slot is reused.

Working bypass (not a fix)

Drop the parallel-route slot entirely and use a Client Component in the layout that reads usePathname():

"use client"
import { usePathname } from "next/navigation"

export function ClientBreadcrumb() {
  const pathname = usePathname()
  return <div>client: {pathname}</div>
}

This works because the React hook reads live state on every render, bypassing whatever cache is holding the stale slot output. Trade-off: any per-URL server work the slot was doing (DB lookups, header reads, etc.) has to move to a client fetch.

Additional notes

  • Same behavior in next dev and next build + next start. Both use Turbopack in Next 16.
  • proxy.ts (Next 16's renamed middleware) runs on every request and x-pathname is set correctly — verified via response headers. The slot's default.tsx simply never executes the second time.
  • No experimental.staleTimes configured. Default is dynamic: 0 in Next 15+, so the client router cache should not be holding dynamic results.
  • Not specific to headers() — same staleness occurs if the slot reads anything else that varies per URL.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
Binaries:
  Node: 22.22.0
  pnpm: 10.31.0
Relevant Packages:
  next: 16.2.4
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Also reproduced against `[email protected]` + `[email protected]` (verified 2026-06-05) in a throwaway copy of the same repo.

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

Parallel & Intercepting Routes, Turbopack

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

next dev (local), next build (local), next start (local), Vercel (Deployed)

Additional context

Originally observed on the Label Backstage production app (Vercel, Next 16.2.4). Four PRs were spent inside the slot trying the documented invalidation patterns before we gave up and switched to the Client Component bypass:

No experimental.staleTimes set. No custom router cache config. The (group) directory is otherwise vanilla App Router. The only Server Component work in the slot was await headers() to read x-pathname — minimal.

Did not bisect canary releases to find the regression window — the bug is present in 16.2.4 and 16.3.0-canary.41, so this isn't a recent regression. Happy to bisect if it would help.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING