vercel / vercel/next.js

[Turbopack] Shared library CSS module duplicated & re-ordered after client navigation in dev, breaking cascade order

Open
#94,980 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Turbopack
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/ilchenkoArtem/turbopack-css-order-repro

To Reproduce
  1. npm install
  2. npm run dev (runs nx serve webnext dev --turbopack, App Router)
  3. Open the home page. The "Show more" button is transparent (white border, white text) — correct.
  4. Click the button → client navigation to /catalog.
  5. Press the browser Back button.
  6. The button is now orange — wrong.
Current vs. Expected behavior

A shared Button component lives in a separate library (libs/ui-kit, imported via the @ui-kit/* tsconfig path alias + experimental.externalDir, built through @nx/next's withNx). Its .kindPrimary class sets --btn-bg: #fd9728 (orange). The app's Banner adds .featureButton which overrides --btn-bg: transparent. Both selectors have specificity (0,1,0), so the cascade is decided purely by document order, and the override is authored to win by coming later.

Current: On the client navigation to /catalog, Turbopack loads a second copy of the shared Button stylesheet (e.g. _0fllvkn._.css) and appends it to <head>. On Back this duplicate is not removed, so it ends up after the home chunk that contains .featureButton. The later .kindPrimary now wins → the button turns orange.

DOM after Back (both stylesheets define --btn-bg):

_12prpzp._.css   .button {…}  .kindPrimary {--btn-bg:#fd9728}  .featureButton {--btn-bg:transparent}
_0fllvkn._.css   .button {…}  .kindPrimary {--btn-bg:#fd9728}   ← appended on nav, not removed → wins

Expected: CSS module cascade order stays stable across client navigations (as it does under webpack), so the override keeps winning and the button stays transparent.

This is incorrect styling in next dev only, surfaced by client-side navigation. A hard reload of the same URL renders correctly. (Distinct from #83941, which reports a dev-vs-next build discrepancy; here next dev itself renders the wrong order after a soft navigation.)

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0
  Available memory (MB): 36864
  Available CPU cores: 12
Binaries:
  Node: 22.15.1
  npm: 10.9.2
  pnpm: 10.15.0
Relevant Packages:
  next: 16.2.3 (also reproduced on 16.2.9 and 16.3.0-canary.58)
  react: 19.2.7
  react-dom: 19.2.7
  typescript: 5.9.3
Next.js Config:
  output: N/A
Which area(s) are affected? (Select all that apply)

Turbopack

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

next dev (local)

Additional context
  • Open the repro in CodeSandbox (Devbox — runs real Turbopack): https://codesandbox.io/p/github/ilchenkoArtem/turbopack-css-order-repro
  • Reproduced on next@16.2.3, 16.2.9, and the latest canary 16.3.0-canary.58.
  • Conditions required to trigger (each matters):
    • Built through @nx/next's withNx (this is an Nx monorepo). Plain next dev in a single-package app batches the CSS into one chunk and does not reproduce.
    • The shared component is outside the app dir (libs/ui-kit, reached via path alias + experimental.externalDir), so its CSS module is emitted as a standalone chunk.
    • Enough routes share the component that Turbopack gives it its own chunk (/r1..r16 + /catalog in the repro).
  • Workaround (not a fix): raising the override's specificity to (0,2,0) (.featureButton.featureButton) makes it win regardless of chunk order. The underlying issue is that equal-specificity CSS-module ordering is not preserved across client navigations under Turbopack.
  • Replaces #94978, which was auto-closed because its reproduction section contained more than one link; same repro, link corrected. Likely related to #83941 and PR #89615.

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.

Research direction

Start with the linked turbopack-css-order-repro: run npm install, npm run dev, navigate home → /catalog → Back, and inspect the generated stylesheets in the document head. Trace Turbopack's CSS chunk handling during client navigation; done means the shared stylesheet is not duplicated or left appended, and equal-specificity cascade order remains stable.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, javascript, next.js, react
Domain
build-system, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.