experimental.inlineCss duplicates the full CSS bundle into every RSC/segment payload (standalone build-output bloat)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.5k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/mikaelh/next-inlinecss-rsc-dup-repro
To Reproduce
npm install(pullsnext@canary) thennpm run buildon the linked repo. It is a minimal App Router app:output: "standalone",experimental.inlineCss: true, a 155 KB global stylesheet imported in the root layout, and one[slug]route withgenerateStaticParams→ 60 tiny static pages.- Inspect
.next/standalone/.next/server/app, e.g.grep -rl '\.u1000{' .next/standalone/.next/server/app | wc -land count occurrences per file.
Current vs. Expected behavior
Current (verified on next@16.3.0-canary.66): with inlineCss, the whole stylesheet is inlined into each page's HTML <style> and also serialized into the page's RSC payloads — the inline flight inside the .html, the page.rsc, and each .segments/*.segment.rsc.
Observed in the repro for one trivial static page (a1.html, 411 KB for "tiny content"): 1 <style> block, but the CSS marker rule appears 3× (<style> + flight). Across the build the 155 KB CSS is embedded in 62 .html, 186 .rsc (of which 124 are *.segments/*.segment.rsc) → .next/server/app = 69 MB for 60 pages of "tiny content". So each prerendered page stores the stylesheet ~3–4× (HTML <style> + HTML flight + page.rsc + each segment.rsc); build output scales with CSS_size × pages × ~4.
Amplifier: moving the @import from the root layout into a nested layout (route group) makes the per-segment copies multiply (~2× → ~5× per page in a larger app), so per-route CSS scoping is counter-productive under inlineCss. On a real content site (~700 prerendered pages, 172 KB CSS) the duplicated CSS is ~205 MB of a 453 MB server/app; moving CSS to a nested layout took server/app from 453 MB to 868 MB.
Expected: inlined CSS referenced/deduplicated once per prerendered page rather than copied into every RSC/segment payload. The segment-cache / client-navigation RSC payloads don't need the full stylesheet embedded — on client navigation the browser already has the styles applied. Keep inlining into the initial HTML (the FCP/LCP win) without duplicating across the RSC.
Provide environment information
Operating System: Linux
Binaries:
Node: 24.x
Relevant Packages:
next: 16.3.0-canary.66
react: 19.2.0
react-dom: 19.2.0
Next.js Config:
output: "standalone"
experimental.inlineCss: true
Which area(s) are affected? (Select all that apply)
Output (Standalone), Partial Prerendering (PPR)
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
inlineCss is otherwise a real FCP/LCP win, so disabling it is an unwanted trade-off — the request is to keep the initial-HTML inline but stop duplicating the bundle across the RSC/segment payloads. Measured via du + counting the inlined CSS chunk across .html / .rsc / .segments/*.segment.rsc. (Supersedes #95140, which was auto-closed for a parsing issue — the reproduction link is now in the correct field.)
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.
Research direction
Run the linked reproduction with npm install and npm run build, then inspect .next/standalone/.next/server/app, including the generated .html, .rsc, and .segments/*.segment.rsc files. Trace experimental.inlineCss through the build output and RSC payload generation; done means CSS remains in the initial HTML without being duplicated across the page and segment payloads.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, next.js, react
- Domain
- build-system, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100