Skip to content

fix: CSS modules not working in _app/_layout/_error and across routes in dev#3764

Merged
bartlomieju merged 3 commits intomainfrom
fix/css-modules-compilation
Apr 9, 2026
Merged

fix: CSS modules not working in _app/_layout/_error and across routes in dev#3764
bartlomieju merged 3 commits intomainfrom
fix/css-modules-compilation

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

@bartlomieju bartlomieju commented Apr 9, 2026

Islands used in _app.tsx, _layout.tsx, or _error.tsx are discovered after <head> has already rendered, so their CSS <link> tags were never injected. FreshScripts (at end of <body>) now emits late CSS for any islands missed by RemainingHead, using an injectedCss set to avoid duplicates.

In dev mode, collectCss only walked the fresh:client-entry module graph, missing CSS imported by island modules. The dev server now also walks fresh-island::* module graphs when collecting CSS.

Note: late-injected <link> tags in <body> may cause a brief flash of unstyled content — this is inherent to the problem since <head> renders before <body> islands are discovered.

Closes #3633

Test plan

  • All existing island/CSS tests pass
  • New: css modules in _app.tsx island are injected (build + dev)
  • New: css modules work on second page with shared island (build + dev)

🤖 Generated with Claude Code

bartlomieju and others added 3 commits April 9, 2026 13:04
#3633)

Fixes two CSS modules issues:

1. Islands in _app.tsx, _layout.tsx, or _error.tsx had their CSS
   missing because they were discovered after <head> was already
   rendered. Now FreshScripts (at end of <body>) emits <link> tags
   for any island CSS that wasn't already injected in <head>.

2. In dev mode, CSS modules for islands shared across multiple
   routes only worked on the first page visited. The dev server's
   CSS collection now also walks island module graphs in addition
   to the client entry, ensuring all island CSS is collected
   regardless of which route is accessed first.

Closes #3633

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds an AppNav island with a CSS module to _app.tsx and a second
page that shares the CssModules island. Tests verify:

1. CSS modules from islands in _app.tsx are injected into the page
   (both build and dev mode)
2. CSS modules from shared islands work on the second page visited,
   not just the first (both build and dev mode)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju merged commit 36dd637 into main Apr 9, 2026
15 of 16 checks passed
@bartlomieju bartlomieju deleted the fix/css-modules-compilation branch April 9, 2026 15:37
@Hajime-san
Copy link
Copy Markdown

Hajime-san commented Apr 20, 2026

@bartlomieju
Thank you for fixing this! Could you cut a release? We needs this feature.

bartlomieju added a commit that referenced this pull request Apr 27, 2026
- Bump `@fresh/core` to `2.3.1`
- Bump `@fresh/init` to `2.3.1`
- Bump `@fresh/update` to `2.3.1`
- Bump `@fresh/plugin-vite` from `1.0.8` to `1.1.0`
- Bump `@fresh/plugin-tailwind-v3` from `1.0.1` to `1.1.0`

The plugin packages were not published during the 2.3.0 release despite
having significant changes merged. Notable fixes in `@fresh/plugin-vite`
include immutable caching for Vite-built assets (#3761), CSS modules in
layouts (#3764), and multiple `staticDir` support (#3759).

Also updates `FRESH_VITE_PLUGIN` version constant in `@fresh/init` so
new projects scaffold with the correct plugin version.

Closes #3784
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSS Modules compilation issues

2 participants