Skip to content

Avoid duplicate css during dev due to server rendered <link> and client inline <style> #677

@hi-ogawa

Description

@hi-ogawa

Moved from hi-ogawa/vite-plugins#969


I just saw redwoodjs/sdk#625, which implements client reference css by switching <link> (prod) and <style> (dev). For server component css, we can probably do the same (i.e. switch to inline <style>) since we collect css in rsc environment module graph and manually inject <link> elements:

React.createElement('link', {
key: 'css:' + href,
rel: 'stylesheet',
precedence: 'vite-rsc/importer-resources',
href: href,
}),

However as wrote in hi-ogawa/vite-plugins#969 (comment), it might be difficult for client reference css since we currently rely on ReactDOM.preinitStyle, which only supports <link> injection. To replace this with inline <style>, we would either

  • new react API to hoist up inline <style> during ssr
  • inject <style> element via proxy getter (similar to Parcel's module proxy)
  • avoid client reference preload level injection and switch to rscCssTransform like injection

Also we also should solve potential FOUC of lazy component css

For this we might need to switch to rscCssTransform based approach for client reference css anyways.

Also not sure how we can pass around nonce for inline style. We might need dedicated async local storage internally?


Also we might not have to worry about this at all by accepting temporary duplicate css, but just remove <link> as early as possible when corresponding <style> is injected on client, namely dealing with this TODO:

// TODO: this doesn't have to wait for "vite:beforeUpdate" and should do it right after browser css import.
code += /* js */ `
const ssrCss = document.querySelectorAll("link[rel='stylesheet']");
import.meta.hot.on("vite:beforeUpdate", () => {
ssrCss.forEach(node => {
if (node.dataset.precedence?.startsWith("vite-rsc/")) {
node.remove();
}
});
});
`

Can this be achieved somehow by mutation observer? or some (new) API for import.meta.hot event?

Oh, but one thing to worry about is that, after <link> is manually removed, I've always wondered why React doesn't add them back again after seeing new RSC payload including the same <link> tags. If they start to do at some point, then manual removal doesn't help (though maybe the same can be said for inline <style>).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions