Skip to content

Fix css_module stylesheets missing from SSR head after the first request - #5707

Open
nicoburns wants to merge 1 commit into
mainfrom
devin/1785038100-css-module-per-document-link
Open

Fix css_module stylesheets missing from SSR head after the first request#5707
nicoburns wants to merge 1 commit into
mainfrom
devin/1785038100-css-module-per-document-link

Conversation

@nicoburns

@nicoburns nicoburns commented Jul 26, 2026

Copy link
Copy Markdown
Member

Summary

Fixes DioxusLabs/dioxus-components#287 ("component rendering takes a while after reloading but is instantaneous after building").

The #[css_module]-generated __CssIdent::deref injected its stylesheet via document().create_link(...) guarded by a process-global static OnceLock. On a fullstack server the ServerDocument collecting head elements is per-request, so only the first request after the server boots got the <link rel="stylesheet"> in the SSR <head>. Every subsequent request (i.e. any page reload) was served with no stylesheet link at all, and the page rendered as unstyled text until WASM hydration re-inserted the link client-side.

Reproduced with a minimal fullstack app: the first curl of / contained the stylesheet link in the head; the second did not.

The fix deduplicates per document (per VirtualDom) instead of per process:

  • New dioxus_document::insert_stylesheet(href) helper that reuses the same root-scope LinkContext dedup (keyed "{href}|stylesheet") as the document::Link component, then gates on create_head_component() so hydration doesn't insert a duplicate link when the server already rendered it:
    pub fn insert_stylesheet(href: &str) {
        // dedup first so repeated calls (deref runs on every render)
        // don't create/consume extra hydration entries
        if !should_insert_link(href, Some("stylesheet")) { return; }
        let document = document();
        if !document.create_head_component() { return; }
        document.create_link(/* rel="stylesheet" href={href} */);
    }
  • The css_module macro's generated Deref now just calls dioxus::document::insert_stylesheet(&ASSET.to_string()) instead of the OnceLock block.

With this, every SSR request emits the component stylesheet in the head, and hydration skips re-inserting it (previously the client always appended a duplicate link).

Verified: curling a fullstack repro repeatedly now returns the <link rel="stylesheet"> in the head on every request; cargo clippy --workspace --all-features --all-targets -D warnings and cargo fmt --check pass.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/05fa859d90474349a5e9f0880dddbda0
Requested by: @nicoburns


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Open in Devin Review (Staging)

@nicoburns
nicoburns requested a review from a team as a code owner July 26, 2026 03:58
@nicoburns nicoburns self-assigned this Jul 26, 2026
@staging-devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@staging-devin-ai-integration

Copy link
Copy Markdown

Tested this end-to-end with a minimal fullstack repro of DioxusLabs/dioxus-components#287 (one #[css_module]-styled div, path-dep on this branch). All checks passed.

SSR regression (the reported bug): 3/3 consecutive requests to a freshly booted server each contained exactly one <link rel="stylesheet"> in the <head> — previously only the first request after boot got the link.

Browser (dx serve, fullstack + wasm hydration): page renders styled on first load and stays styled across reloads (2nd+ SSR requests = the bug scenario), with exactly one stylesheet link after hydration (verified window.hydration_callback ran) and a clean console.

Styled after reload with active hydration

Zoom of css_module-styled element + first-load screenshot

zoom - red text = stylesheet applied
first load

@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1785038100-css-module-per-document-link branch from 3dc7e1b to ee9a5b8 Compare July 26, 2026 04:36
@mcmah309

Copy link
Copy Markdown
Contributor

Related #5644

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.

coponent rendering takes a while after reloading but is instantainiuos after building

3 participants