PageSourceSearch

https://shakacode.com/0a6e3696-70be7353577326f2cde6.js

js shakacode.com collected 2026-10-04 00:28:05 UTC 192,805 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkgatsby_starter_default=self.webpackChunkgatsby_starter_default||[]).push([[2895],{80901:function(e,n,a){var t=a(28453),s=a(96540);function l(e){const n=Object.assign({h1:"h1",a:"a",span:"span",p:"p",h2:"h2",table:"table",thead:"thead",tr:"tr",th:"th",tbody:"tbody",td:"td",h3:"h3",ol:"ol",li:"li",strong:"strong",em:"em",ul:"ul",div:"div",svg:"svg",path:"path",h4:"h4"},(0,t.R)(),e.components);return s.createElement(s.Fragment,null,s.createElement(n.h1,{id:"css-and-styling-with-react-server-components",style:{position:"relative"}},s.createElement(n.a,{href:"#css-and-styling-with-react-server-components","aria-label":"css and styling with react server components permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"CSS and Styling with React Server Components"),"\n",s.createElement(n.p,null,"This guide documents how CSS works across Server Components, Client Components, and traditional SSR in\nReact on Rails Pro. It covers the three-bundle CSS architecture, the FOUC prevention pipeline, and\nper-approach setup guidance for every major CSS strategy."),"\n",s.createElement(n.h2,{id:"quick-reference",style:{position:"relative"}},s.createElement(n.a,{href:"#quick-reference","aria-label":"quick reference permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Quick reference"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Approach"),s.createElement(n.th,null,"Server Component"),s.createElement(n.th,null,"Client Component (RSC)"),s.createElement(n.th,null,"Traditional SSR"),s.createElement(n.th,null,"FOUC prevention"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#global-css"},"Global CSS")),s.createElement(n.td,null,"Use class names; CSS loads from layout"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Rails layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}}))),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#css-modules"},"CSS Modules")),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals</code>'}})," renders class names"),s.createElement(n.td,null,"Full extraction + chunk CSS"),s.createElement(n.td,null,"Full extraction"),s.createElement(n.td,null,"RSC client-chunk links")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#sassscss"},"SCSS Modules")),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"RSC client-chunk links")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#tailwind-css"},"Tailwind CSS")),s.createElement(n.td,null,"Use utility classes; CSS loads from layout"),s.createElement(n.td,null,"Use utility classes"),s.createElement(n.td,null,"Use utility classes"),s.createElement(n.td,null,"Rails layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}}))),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#inline-styles"},"Inline styles")),s.createElement(n.td,null,"Works (serialized in RSC payload)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"N/A (no external CSS)")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#vanilla-extract"},"Vanilla Extract")),s.createElement(n.td,null,"Needs client-boundary wrapper"),s.createElement(n.td,null,"Works with build plugin"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"RSC client-chunk links")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#styled-components"},"styled-components")),s.createElement(n.td,null,"Not supported"),s.createElement(n.td,null,"Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})),s.createElement(n.td,null,"Works with SSR setup"),s.createElement(n.td,null,"None (runtime injection)")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#emotion"},"Emotion")),s.createElement(n.td,null,"Not supported"),s.createElement(n.td,null,"Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})),s.createElement(n.td,null,"Works with SSR setup"),s.createElement(n.td,null,"None (runtime injection)")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.a,{href:"#other-static-extraction-libraries"},"Other static extraction")),s.createElement(n.td,null,"Expected to work via layout CSS"),s.createElement(n.td,null,"Expected to work"),s.createElement(n.td,null,"Expected to work"),s.createElement(n.td,null,"Depends on setup")))),"\n",s.createElement(n.p,null,"Status: entries marked with specific verification notes below. See the ",s.createElement(n.a,{href:"#compatibility-matrix"},"full compatibility matrix")," for details."),"\n",s.createElement(n.h2,{id:"how-css-reaches-the-browser",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#how-css-reaches-the-browser","aria-label":"how css reaches the browser permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"How CSS reaches the browser"),"\n",s.createElement(n.p,null,"This section explains the whole system from first principles, assuming you know the traditional SSR\nmodel but have never looked inside RSC streaming. It is grounded in the actual implementation\n(file and function references are given so you can verify against the source)."),"\n",s.createElement(n.h3,{id:"three-browser-rules-everything-is-built-on",style:{position:"relative"}},s.createElement(n.a,{href:"#three-browser-rules-everything-is-built-on","aria-label":"three browser rules everything is built on permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Three browser rules everything is built on"),"\n",s.createElement(n.p,null,"The entire FOUC story — in both traditional SSR and streamed RSC — reduces to three rules of\nbrowser behavior:"),"\n",s.createElement(n.ol,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"A parser-created ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="stylesheet"></code>'}})," in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," is render-blocking.")," The browser\npaints nothing until every head stylesheet has loaded. This is why traditional SSR has no FOUC:\nput all the CSS in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),", and the first paint is styled by construction."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"A stylesheet link in the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;body></code>'}})," does not block paint of content parsed before it — but\nwhile it is loading, the browser will not execute any later parser-inserted ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;script></code>'}})),'\n(the HTML spec\'s "a style sheet that is blocking scripts" rule). Chromium additionally delays\npainting content that appears ',s.createElement(n.em,null,"after")," an in-body stylesheet link."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="preload" as="style"></code>'}})," only downloads.")," A preload never applies styles and never\nblocks anything."),"\n"),"\n",s.createElement(n.p,null,"Rule 1 protects the initial shell. Rule 2 is the load-bearing mechanism for everything streamed\nafter the shell: React on Rails Pro's streamed-CSS gating works by placing a real stylesheet\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," in the byte stream ",s.createElement(n.em,null,"before")," React's inline reveal script, so the reveal script cannot run\nuntil the CSS has loaded. It is a browser-spec mechanism, not a React feature."),"\n",s.createElement(n.h3,{id:"what-is-in-head-at-first-paint",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#what-is-in-head-at-first-paint","aria-label":"what is in head at first paint permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"What is in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," at first paint"),"\n",s.createElement(n.p,null,"The Rails layout owns ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),". The node renderer (the process that streams React HTML) never\nwrites into it. The head CSS set at first paint is:"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Layout stylesheet tags")," — whatever ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}})," links your layout declares (global\nCSS, Tailwind, design tokens)."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Auto-appended generated component packs")," — for every ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," /\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react_component</code>'}})," call with ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle</code>'}}),", React on Rails calls\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">append_stylesheet_pack_tag("generated/&lt;ComponentName>")</code>'}}),"\n(",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react_on_rails/lib/react_on_rails/helper.rb</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">load_pack_for_generated_component</code>'}}),"), and the\nlayout's argless ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;%= stylesheet_pack_tag %></code>'}})," renders those links in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),". This is the CSS\nextracted for each component's ",s.createElement(n.em,null,"entry")," chunk — the synchronously imported client CSS of each\nstreamed root."),"\n"),"\n",s.createElement(n.p,null,"Rails renders the entire layout (head included) as the ",s.createElement(n.strong,null,"first streamed chunk"),"\n(",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb</code>'}}),",\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_view_containing_react_components</code>'}}),"). By rule 1, that head CSS — and only that CSS — blocks\nfirst paint."),"\n",s.createElement(n.p,null,"What is deliberately ",s.createElement(n.strong,null,"not")," in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," at first paint: CSS for code-split client components\ndeeper in the RSC tree (the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientN</code>'}})," chunks). Putting the whole page's CSS in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," would make\nfirst paint wait for below-the-fold styling — the exact tradeoff streaming exists to avoid. That\nlate CSS is delivered mid-stream instead, which is what the rest of this section is about."),"\n",s.createElement(n.h3,{id:"what-blocks-first-paint--and-what-does-not",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#what-blocks-first-paint--and-what-does-not","aria-label":"what blocks first paint  and what does not permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"What blocks first paint — and what does not"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Blocks first paint"),s.createElement(n.th,null,"Does NOT block first paint"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," stylesheet links (layout + appended generated packs)"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rsc-css</code>'}})," stylesheet links streamed into the body for later boundaries*")),s.createElement(n.tr,null,s.createElement(n.td,null,"Synchronous head scripts (standard browser behavior)"),s.createElement(n.td,null,"RSC payload ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;script></code>'}})," tags (inline data pushes)")),s.createElement(n.tr,null,s.createElement(n.td,null,"—"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">defer</code>'}}),"/",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async</code>'}})," JavaScript bundles")),s.createElement(n.tr,null,s.createElement(n.td,null,"—"),s.createElement(n.td,null,"The Flight (RSC payload) stream itself")),s.createElement(n.tr,null,s.createElement(n.td,null,"—"),s.createElement(n.td,null,"Suspense boundary content still streaming (fallbacks paint first)")))),"\n",s.createElement(n.p,null,"* One qualification: when Flight data arrives before the first HTML flush, the streaming pipeline\nwrites inferred ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rsc-css</code>'}})," links ",s.createElement(n.strong,null,"ahead of the shell markup"),' in the first component chunk (flush\norder is payload-init scripts → stylesheet links → HTML). Because Chromium delays painting content\nthat follows an in-body pending stylesheet, those early links can delay the first visible paint of\nthe shell/fallbacks until that CSS loads. This is the intended trade — that CSS belongs to client\ncomponents rendered in the shell itself, so painting the shell before it would be a FOUC — but it\nmeans "does not block first paint" is guaranteed only for links emitted after the shell chunk.'),"\n",s.createElement(n.h3,{id:"timeline-of-a-streamed-rsc-response",style:{position:"relative"}},s.createElement(n.a,{href:"#timeline-of-a-streamed-rsc-response","aria-label":"timeline of a streamed rsc response permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Timeline of a streamed RSC response"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="mermaid"><pre class="language-mermaid"><code class="language-mermaid"><span class="token keyword">sequenceDiagram</span>\n    <span class="token keyword">autonumber</span>\n    <span class="token keyword">participant</span> B as Browser\n    <span class="token keyword">participant</span> R as Rails&lt;br/>
1<span class="token text string">(stream.rb)</span>\n    <span class="token keyword">participant</span> I as injectRSCPayload&lt;br/><span class="token text string">(node renderer)</span>\n    <span class="token keyword">participant</span> Z as React Fizz&lt;br/><span class="token text string">(HTML render)</span>\n    <span class="token keyword">participant</span> F as React Flight&lt;br/><span class="token text string">(RSC render)</span>\n\n    B<span class="token arrow operator">->></span>R<span class="token operator">:</span> GET /page <span class="token text string">(uncached)</span>\n    R<span class="token arrow operator">--\x3e></span>B<span class="token operator">:</span> Chunk 1 — layout shell<span class="token operator">:</span> head with app CSS links\n    <span class="token keyword">Note over</span> B<span class="token operator">:</span> Rule 1<span class="token operator">:</span> head stylesheets are render-blocking.&lt;br/>Nothing paints until they load.\n    R<span class="token arrow operator">->></span>I<span class="token operator">:</span> stream_react_component → node renderer\n    F<span class="token arrow operator">--\x3e></span>I<span class="token operator">:</span> Flight payload chunks&lt;br/><span class="token text string">(client refs + preinit CSS hint rows)</span>\n    F<span class="token arrow operator">--\x3e></span>Z<span class="token operator">:</span> same payload consumed for SSR&lt;br/><span class="token text string">(hints replay into Fizz)</span>\n    Z<span class="token arrow operator">--\x3e></span>I<span class="token operator">:</span> shell HTML <span class="token text string">(Suspense fallbacks included)</span>\n    I<span class="token arrow operator">--\x3e></span>B<span class="token operator">:</span> <span class="token text string">[payload init scripts]</span><span class="token text string">[inferred rsc-css links]</span><span class="token text string">[shell HTML]</span><span class="token text string">[payload scripts]</span>\n    <span class="token keyword">Note over</span> B<span class="token operator">:</span> FIRST PAINT<span class="token operator">:</span> shell + fallbacks,&lt;br/>styled by head CSS.\n    <span class="token keyword">loop</span> each Suspense boundary that finishes on the server\n        F<span class="token arrow operator">--\x3e></span>I<span class="token operator">:</span> Flight chunk referencing clientN chunks\n        <span class="token keyword">Note over</span> I<span class="token operator">:</span> Path B<span class="token operator">:</span> map clientN → CSS hrefs&lt;br/>via loadable-stats.json\n        Z<span class="token arrow operator">--\x3e></span>I<span class="token operator">:</span> hidden boundary HTML + inline $RC<span class="token text string">( reveal script&lt;br/></span><span class="token text string">(+ link rel=preload for late hint CSS)</span>\n        <span class="token keyword">Note over</span> I<span class="token operator">:</span> Path A<span class="token operator">:</span> promote preload links to stylesheets.&lt;br/>Defer $RC HTML until inference resolves <span class="token text string">(≤100ms)</span>.\n        I<span class="token arrow operator">--\x3e></span>B<span class="token operator">:</span> <span class="token text string">[rsc-css link tags]</span><span class="token text string">[hidden HTML]</span>[$RC<span class="token text string">( script]</span><span class="token text string">[payload scripts]</span>\n        <span class="token keyword">Note over</span> B<span class="token operator">:</span> Rule 2<span class="token operator">:</span> the pending rsc-css stylesheet blocks&lt;br/>the inline $RC script → boundary reveals&lt;br/>only after its CSS has loaded.\n        B<span class="token arrow operator">->></span>B<span class="token operator">:</span> $RC<span class="token punctuation">(</span> swaps fallback → styled content\n    <span class="token keyword">end</span>\n    B<span class="token arrow operator">->></span>B<span class="token operator">:</span> Hydration<span class="token operator">:</span> prepareRSCHydrationRoot moves rsc-css links to head&lt;br/>and waitForGeneratedComponentStylesheets gates the React render</code></pre></div>'}}),"\n",s.createElement(n.p,null,"Key point for readers coming from SSR: the reveal of each streamed boundary is ",s.createElement(n.strong,null,"not")," gated by\nReact knowing about CSS. React's own CSS-aware reveal path (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RR</code>'}})," / ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">completeBoundaryWithStyles</code>'}}),")\nonly engages for ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link precedence="..."></code>'}})," elements rendered ",s.createElement(n.em,null,"inside")," a boundary, which the\nframework's automatic pipeline never does. The gate is byte ordering plus browser rule 2."),"\n",s.createElement(n.h3,{id:"where-the-late-css-comes-from-two-paths-one-bucket",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#where-the-late-css-comes-from-two-paths-one-bucket","aria-label":"where the late css comes from two paths one bucket permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Where the late CSS comes from: two paths, one bucket"),"\n",s.createElement(n.p,null,"CSS for a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component inside the RSC tree is known to the framework in two independent\nways, and both converge on the same ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence="rsc-css"</code>'}})," link format:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="mermaid"><pre class="language-mermaid"><code class="language-mermaid"><span class="token keyword">flowchart</span> TB\n    M<span class="token text string">["react-client-manifest.json&lt;br/>client reference → css: [hrefs]&lt;br/>(built by RSCWebpackPlugin)"]</span>\n    LS<span class="token text string">["loadable-stats.json&lt;br/>assetsByChunkName: clientN → assets&lt;br/>(client build output, copied next to the renderer bundle)"]</span>\n\n    <span class="token keyword">subgraph</span> A<span class="token text string">["Path A — preload-tag promotion"]</span>\n        A1<span class="token text string">["Flight render reads client reference metadata →&lt;br/>manifest Proxy fires preinit(href, precedence: \'rsc-css\')&lt;br/>(react-on-rails-rsc flight-stylesheet-hints.ts)"]</span>\n        A2<span class="token text string">["Hint row rides inside the Flight payload;&lt;br/>replays into the live Fizz request during SSR"]</span>\n        A3<span class="token text string">["Fizz AFTER shell flush: emits only&lt;br/>link rel=preload as=style (non-blocking, rule 3)"]</span>\n        A4<span class="token text string">["injectRSCPayload rewrites the streamed tag:&lt;br/>rel=preload → rel=stylesheet + data-precedence=rsc-css&lt;br/>(applyStreamedStylesheetPreloadGating / promoteStylesheetPreloadTag)"]</span>\n        A1 <span class="token arrow operator">--\x3e</span> A2 <span class="token arrow operator">--\x3e</span> A3 <span class="token arrow operator">--\x3e</span> A4\n    <span class="token keyword">end</span>\n\n    <span class="token keyword">subgraph</span> BP<span class="token text string">["Path B — Flight-chunk inference"]</span>
1\n        B1[<span class="token string">"injectRSCPayload regex-scans raw Flight bytes for&lt;br/>&amp;quot;clientN&amp;quot;,&amp;quot;js/clientN-hash.chunk.js&amp;quot; pairs"</span>]\n        B2<span class="token text string">["Maps chunk name → CSS hrefs via loadable-stats.json&lt;br/>(loadRSCClientChunkStylesheetHrefsByChunkName)"]</span>\n        B3<span class="token text string">["Injects link rel=stylesheet data-precedence=rsc-css&lt;br/>flushed BEFORE the boundary HTML;&lt;br/>defers $RC reveal HTML until inference resolves (≤100ms)"]</span>\n        B1 <span class="token arrow operator">--\x3e</span> B2 <span class="token arrow operator">--\x3e</span> B3\n    <span class="token keyword">end</span>\n\n    M <span class="token arrow operator">--\x3e</span> A1\n    LS <span class="token arrow operator">--\x3e</span> B2\n    A4 <span class="token arrow operator">--\x3e</span> C<span class="token text string">["link rel=stylesheet data-precedence=rsc-css&lt;br/>ahead of the $RC( reveal script in the byte stream"]</span>\n    B3 <span class="token arrow operator">--\x3e</span> C\n    C <span class="token arrow operator">--\x3e</span> G<span class="token text string">["Browser rule 2: pending stylesheet blocks the inline&lt;br/>$RC script → boundary reveals styled"]</span>\n    G <span class="token arrow operator">--\x3e</span> H<span class="token text string">["Hydration: prepareRSCHydrationRoot moves rsc-css links&lt;br/>into head, deduped by href + precedence"]</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"Why two paths exist:"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Path A alone is racy.")," It can only promote a preload tag that Fizz already wrote, which\nrequires the Flight hint to have replayed into the Fizz request before that HTML flushed. If the\nFlight stream lags the HTML stream, the boundary's ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC</code>'}})," script can flush with no preceding link."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Path B closes the race")," by deriving CSS directly from the same Flight bytes the transform is\nembedding into the page, and by holding back the reveal HTML until the inference has had a chance\nto emit links — with a ",s.createElement(n.strong,null,"100ms fail-open timeout")," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RSC_CLIENT_STYLESHEET_INFERENCE_TIMEOUT_MS</code>'}}),"\nin ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload.ts</code>'}}),"). Fail-open means: if Flight data lags more than 100ms behind a reveal,\nthe reveal flushes ungated. A brief FOUC under heavy server delay is a designed-in tail risk, not\na bug in your app."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Both paths routinely fire for the same stylesheet.")," Path B's dedup set is not consulted by\nPath A, so the stream can carry two ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rsc-css</code>'}})," links for one href. This is benign — one network\nfetch (HTTP cache), and the hydration fixup dedupes by href + precedence — but you may notice it\nwhen reading page source."),"\n"),"\n",s.createElement(n.div,{className:"markdown-alert markdown-alert-note",dir:"auto"},"\n",s.createElement(n.p,{className:"markdown-alert-title",dir:"auto"},s.createElement(n.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},s.createElement(n.path,{d:"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"})),"NOTE"),"\n",s.createElement(n.p,null,"RSC CSS collection is request/chunk driven, not a blanket scan of every client reference in the\nmanifest. The injected CSS can still be broader than a single component when a rendered client chunk\ncontains shared, vendor, or page-specific global CSS. Keep client boundaries thin so the chunks\nreferenced by a page do not drag unrelated CSS into the render-blocking ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rsc-css</code>'}})," group."),"\n"),"\n",s.createElement(n.div,{className:"markdown-alert markdown-alert-caution",dir:"auto"},"\n",s.createElement(n.p,{className:"markdown-alert-title",dir:"auto"},s.createElement(n.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},s.createElement(n.path,{d:"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"})),"CAUTION"),"\n",s.createElement(n.p,null,"These stylesheet links are render-blocking for the streamed content that follows them. Broad\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," entry points that import page-specific global CSS can make unrelated RSC pages wait\non that CSS even when they do not visually need it. Prefer thin client wrappers, CSS Modules,\nTailwind utilities, or layout-level global CSS for styles that are truly shared across pages."),"\n",s.createElement(n.p,null,"Contaminated global CSS can also win source-order ties because the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rsc-css</code>'}})," links end up after\nearlier Rails layout styles in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),". Avoid bare element selectors in component stylesheets; if\napp globals must override framework CSS, make that specificity explicit in the app's global\nstylesheet."),"\n"),"\n",s.createElement(n.h3,{id:"module-map-everything-that-touches-css-during-rsc-streaming",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#module-map-everything-that-touches-css-during-rsc-streaming","aria-label":"module map everything that touches css during rsc streaming permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Module map: everything that touches CSS during RSC streaming"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="mermaid"><pre class="language-mermaid"><code class="language-mermaid"><span class="token keyword">flowchart</span> LR\n    <span class="token keyword">subgraph</span> BT<span class="token text string">["Build time"]</span>\n        RWP<span class="token text string">["RSCWebpackPlugin&lt;br/>(react-on-rails-rsc)"]</span> <span class="token arrow operator">--\x3e</span> RCM<span class="token text string">["react-client-manifest.json&lt;br/>css array per client reference"]</span>\n        CWB<span class="token text string">["Client webpack/rspack build"]</span> <span class="token arrow operator">--\x3e</span> LST<span class="token text string">["loadable-stats.json&lt;br/>assetsByChunkName"]</span>\n        CWB <span class="token arrow operator">--\x3e</span> SMF<span class="token text string">["Shakapacker manifest.json"]</span>\n    <span class="token keyword">end</span>\n\n    <span class="token keyword">subgraph</span> RB<span class="token text string">["Rails (Ruby)"]</span>\n        HLP<span class="token text string">["helper.rb&lt;br/>load_pack_for_generated_component"]</span> <span class="token arrow operator">--\x3e</span><span class="token label property">|"append_stylesheet_pack_tag"|</span> HED<span class="token text string">["Layout head CSS links&lt;br/>(first streamed chunk)"]</span>\n        PRH<span class="token text string">["pro_helper.rb&lt;br/>generated_stylesheet_hrefs_json"]</span> <span class="token arrow operator">--\x3e</span><span class="token label property">|"data-generated-stylesheet-hrefs"|</span> SPT<span class="token text string">["Component spec script tag"]</span>\n        STR<span class="token text string">["stream.rb&lt;br/>stream_view_containing_react_components"]</span> <span class="token arrow operator">--\x3e</span> HED\n    <span class="token keyword">end</span>\n\n    <span class="token keyword">subgraph</span> NR<span class="token text string">["Node renderer (TypeScript)"]</span>\n        WSH<span class="token text string">["flight-stylesheet-hints.ts&lt;br/>withStylesheetHints Proxy → preinit"]</span> <span class="token arrow operator">--\x3e</span> FLP<span class="token text string">["Flight payload&lt;br/>(data + CSS hint rows)"]</span>\n        FLP <span class="token arrow operator">--\x3e</span> FIZ<span class="token text string">["react-dom Fizz SSR&lt;br/>preamble styles / late preloads / $RC scripts"]</span>\n        FIZ <span class="token arrow operator">--\x3e</span> INJ<span class="token text string">["injectRSCPayload.ts&lt;br/>promotion + inference + reveal deferral"]</span>\n        FLP <span class="token arrow operator">--\x3e</span> INJ\n    <span class="token keyword">end</span>\n\n    <span class="token keyword">subgraph</span> BR<span class="token text string">["Browser (TypeScript)"]</span>\n        CSR<span class="token text string">["ClientSideRenderer.ts&lt;br/>waitForGeneratedComponentStylesheets"]</span>\n        PHR<span class="token text string">["rscHydrationDom.ts&lt;br/>prepareRSCHydrationRoot"]</span>\n        GRC<span class="token text string">["getReactServerComponent.client.ts&lt;br/>payload replay → preinit inserts head links"]</span>\n    <span class="token keyword">end</span>\n\n    SMF <span class="token arrow operator">--\x3e</span> HLP\n    SMF <span class="token arrow operator">--\x3e</span> PRH\n    RCM <span class="token arrow operator">--\x3e</span> WSH\n    LST <span class="token arrow operator">--\x3e</span><span class="token label property">|"chunk name → CSS hrefs"|</span> INJ\n    INJ <span class="token arrow operator">--\x3e</span><span class="token label property">|"rsc-css links + HTML + payload scripts"|</span> PHR\n    SPT <span class="token arrow operator">--\x3e</span><span class="token label property">|"hrefs to await before hydration"|</span> CSR\n    INJ <span class="token arrow operator">--\x3e</span><span class="token label property">|"embedded Flight payload"|</span> GRC</code></pre></div>'}}
1),"\n",s.createElement(n.p,null,"Data handoffs to know by name:"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Data"),s.createElement(n.th,null,"Producer"),s.createElement(n.th,null,"Consumer"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," arrays in the RSC client manifest"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RSCWebpackPlugin</code>'}})," (react-on-rails-rsc)"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">withStylesheetHints</code>'}})," Proxy → ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">preinit</code>'}})," hint rows")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">loadable-stats.json</code>'}})),s.createElement(n.td,null,"Client build (must be copied next to the renderer bundle)"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload</code>'}})," chunk-name → CSS map (Path B)")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence="rsc-css"</code>'}})," links"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload</code>'}})," (both paths)"),s.createElement(n.td,null,"Browser rule 2 gating; ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">prepareRSCHydrationRoot</code>'}})," head move")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-generated-stylesheet-hrefs</code>'}})),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">pro_helper.rb</code>'}})," (requires ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle</code>'}}),")"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">waitForGeneratedComponentStylesheets</code>'}})," pre-hydration wait")),s.createElement(n.tr,null,s.createElement(n.td,null,"Embedded Flight payload scripts"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload</code>'}})),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactServerComponent.client.ts</code>'}})," hydration replay")))),"\n",s.createElement(n.h3,{id:"what-makes-a-streamed-component-wait-for-its-own-css",style:{position:"relative"}},s.createElement(n.a,{href:"#what-makes-a-streamed-component-wait-for-its-own-css","aria-label":"what makes a streamed component wait for its own css permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"What makes a streamed component wait for its own CSS"),"\n",s.createElement(n.p,null,"To answer it in one place, because it is the most commonly misunderstood part:"),"\n",s.createElement(n.ol,null,"\n",s.createElement(n.li,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload</code>'}})," guarantees a parser-created ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="stylesheet" data-precedence="rsc-css"></code>'}}),"\nappears in the byte stream ",s.createElement(n.strong,null,"before")," the boundary's inline ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC(</code>'}})," reveal script (via Path A\npromotion in place, and/or Path B injection plus reveal deferral)."),"\n",s.createElement(n.li,null,"Browser rule 2 does the rest: the pending stylesheet blocks execution of the inline script, so\nthe hidden boundary HTML is not swapped in until the CSS has loaded."),"\n",s.createElement(n.li,null,"After the swap, at hydration time, ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">prepareRSCHydrationRoot</code>'}})," moves those links into ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}
1),"\n(so React's hydration does not see unexpected nodes in the component container), and\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">waitForGeneratedComponentStylesheets</code>'}})," additionally waits (up to 10s) for the component's\ngenerated-pack stylesheets before the React render begins — this protects the client-render\npath (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">prerender: false</code>'}}),"), where React would otherwise insert DOM before head CSS finished\nloading."),"\n"),"\n",s.createElement(n.p,null,"React's own commit-blocking for precedence stylesheets applies only on the browser-side payload\nreplay path (client-inserted styles during hydration/navigation), not to the server-streamed reveal."),"\n",s.createElement(n.h3,{id:"when-the-fouc-pipeline-silently-does-nothing",style:{position:"relative"}},s.createElement(n.a,{href:"#when-the-fouc-pipeline-silently-does-nothing","aria-label":"when the fouc pipeline silently does nothing permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"When the FOUC pipeline silently does nothing"),"\n",s.createElement(n.p,null,"Each of these conditions disables part or all of the streamed-CSS gating ",s.createElement(n.strong,null,"without any error"),".\nThe page still works — CSS eventually arrives via the browser-side payload replay after hydration —\nbut the streamed reveal is no longer gated, so a styled-late flash becomes possible. Two of these\nconditions are not edge cases but the ",s.createElement(n.strong,null,"production defaults verified in the dummy app")," (numeric\nchunk ids from ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">chunkIds: 'deterministic'</code>"}}),", and id-named extracted CSS like\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css/[id]-[hash].css</code>'}}),") — verified against a production dummy build on 2026-07-09, where the\nstreamed reveal was confirmed ungated under both bundlers:"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Condition"),s.createElement(n.th,null,"What is disabled"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">loadable-stats.json</code>'}})," missing/unreadable next to the renderer bundle (retries 100ms → 30s backoff)"),s.createElement(n.td,null,"Path B entirely, including reveal deferral; Path A still runs")),s.createElement(n.tr,null,s.createElement(n.td,null,"Chunk names not matching ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">client&lt;N></code>'}})," (custom ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">chunkName</code>'}})," option, or numeric webpack ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">chunkIds</code>'}}),")"),s.createElement(n.td,null,"Path B (Flight regex never matches); Path A then depends on manifest-backed preload matching")),s.createElement(n.tr,null,s.createElement(n.td,null,"CSS output filenames not matching ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css/clientN-*.css</code>'}})),s.createElement(n.td,null,"Path A's ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientN</code>'}})," filename fallback (manifest-listed preloads can still promote)")),s.createElement(n.tr,null,s.createElement(n.td,null,"Flight data lagging a reveal by more than 100ms"),s.createElement(n.td,null,"Reveal deferral for that flush (fail-open by design)")),s.createElement(n.tr,null,s.createElement(n.td,null,"Rs
1pack builds omitting CSS assets from the stats consumed by ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">injectRSCPayload</code>'}})),s.createElement(n.td,null,"Path B (empty chunk→CSS map) — see Known limitations")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle</code>'}})," off (or non-Pro)"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-generated-stylesheet-hrefs</code>'}})," + the pre-hydration stylesheet wait")),s.createElement(n.tr,null,s.createElement(n.td,null,"React configured with the external Fizz runtime (no inline ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC(</code>'}})," scripts)"),s.createElement(n.td,null,"Reveal-split detection — deferral silently never fires")))),"\n",s.createElement(n.p,null,"If you are debugging a flicker, check these in order; the first two are by far the most common."),"\n",s.createElement(n.h3,{id:"coupling-to-react-internals-what-breaks-where",style:{position:"relative"}},s.createElement(n.a,{href:"#coupling-to-react-internals-what-breaks-where","aria-label":"coupling to react internals what breaks where permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Coupling to React internals (what breaks where)"),"\n",s.createElement(n.p,null,"The streamed-CSS gating reads React's Fizz output as text. These are the assumptions, all in\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">packages/react-on-rails-pro/src/injectRSCPayload.ts</code>'}})," unless noted:"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC(</code>'}})," reveal-script literal")," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">REACT_SUSPENSE_REVEAL_SCRIPT</code>'}}),") — assumes Fizz inlines a\ncompletion script calling ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC("...")</code>'}}),". The reveal implementation already changed shape within\nReact 19 (19.2 batches reveals through ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RB</code>'}}),"/",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RV</code>'}}),"), but the literal survived. React's external\nFizz runtime option would remove inline scripts entirely and silently disable reveal deferral."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Hidden boundary markup")," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">findReactSuspenseRevealSplitIndex</code>'}}),") — assumes completed segments\narrive as ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;div hidden id="..."></code>'}}),". Non-",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">div</code>'}})," segment containers (table contexts) fall back to\nsplitting at the script tag — degraded, not broken."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Flight wire format")," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RSC_CLIENT_CHUNK_NAME_WITH_JS_ASSET</code>'}}),") — assumes client-reference\nmetadata serializes as adjacent ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">"clientN","js/clientN-&lt;hash>.chunk.js"</code>'}})," string pairs in the\npayload text. This is React's internal wire protocol plus the bundler's naming convention."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">destination.flush()</code>'}})," signal")," — an internal Fizz convention used for chunk timing; a\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">setTimeout(0)</code>'}})," fallback means failure degrades to coarser chunking, not breakage."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence</code>'}})," attribute mechanics")," — the precedence ",s.createElement(n.em,null,"feature")," is documented React 19 API;\nthe attribute name and hydration adoption mechanics are implementation details, matched in\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rscDomMarkers.ts</code>'}})," / ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rscHydrationDom.ts</code>'}}),"."),"\n"),"\n",s.createElement(n.p,null,"Re-verify these against Fizz output on every React upgrade, including minors."),"\n",s.createElement(n.h3,{id:"what-this-means-for-different-css-approaches",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#what-this-means-for-different-css-approaches","aria-label":"what this means for different css approaches permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"What this means for different CSS approaches"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Build-time CSS")," (CSS Modules, SCSS, Tailwind, Vanilla Extract) is extracted into files by\nthe client bundle. If the import is behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),", the extracted CSS file appears in\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}})," and gets FOUC prevention. If the import is in a global/layout pack,\nFOUC prevention comes from the Rails layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tag."),"\n"),"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Runtime CSS-in-JS")," (styled-components, Emotion) injects CSS via ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;style></code>'}})," tags at runtime.\nTheir CSS is ",s.createElement(n.strong,null,"not")," in extracted files and ",s.createElement(n.strong,null,"not")," in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}}),". There is no\nFOUC prevention from the manifest pipeline for these approaches."),"\n"),"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Inline styles")," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">style</code>'}})," prop) are serialized directly in the HTML or RSC payload. No external\nCSS file is needed, so FOUC is not a concern."),"\n"),"\n"),"\n",s.createElement(n.h2,{id:"three-bundle-css-architecture",style:{position:"relative"}},s.createElement(n.a,{href:"#three-bundle-css-architecture","aria-label":"three bundle css architecture permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Three-bundle CSS architecture"),"\n",s.createElement(n.p,null,"React on Rails Pro builds three webpack/Rspack graphs for an RSC app. Each handles CSS differently:"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Bundle"),s.createElement(n.th,null,"Runtime"),s.createElement(n.th,null,"CSS handling"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.strong,null,"Client")),s.createElement(n.td,null,"Browser"),s.createElement(n.td,null,"CSS is extracted by ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">MiniCssExtractPlugin</code>'}})," (webpack) or Rspack's built-in CSS extraction. The RSC manifest plugin records CSS files for each ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," module.")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.strong,null,"Server")," (SSR)"),s.createElement(n.td,null,"Node renderer VM"),s.createElement(n.td,null,"CSS extraction is disabled. CSS Modules use ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals: true</code>'}})," in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css-loader</code>'}}),", which emits only the class-name-to-hash mapping without any CSS output. Plain CSS imports become empty modules.")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.strong,null,"RSC")),s.createElement(n.td,null,"Node renderer VM"),s.createElement(n.td,null,"Same CSS handling as the server bundle, plus the RSC loader transforms ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," modules into client references. No browser CSS is extracted.")))),"\n",s.createElement(n.p,null,"The key insight: ",s.createElement(n.strong,null,"only the client bundle produces browser-loadable CSS"),". The server and RSC bundles\nneed just enough CSS processing to render correct class names during SSR, but they never emit\nstylesheets."),"\n",s.createElement(n.div,{className:"markdown-alert markdown-alert-note",dir:"auto"},"\n",s.createElement(n.p,{className:"markdown-alert-title",dir:"auto"},s.createElement(n.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},s.createElement(n.path,{d:"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"})),"NOTE"),"\n",s.createElement(n.p,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass-loader</code>'}})," and ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">postcss-loader</code>'}})," still run in the server and RSC bundles because ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css-loader</code>'}}),"\nneeds valid CSS input to parse class names from CSS Modules. This means SCSS compilation and\nPostCSS processing (including Tailwind) run in all three builds, but only the client build\nproduces CSS output."),"\n"),"\n",s.createElement(n.h2,{id:"where-to-import-css",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#where-to-import-css","aria-label":"where to import css permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Where to import CSS"),"\n",s.createElement(n.h3,{id:"server-components",style:{position:"relative"}},s.createElement(n.a,{href:"#server-components","aria-label":"server components permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Server Components"),"\n",s.createElement(n.p,null,"Server Components render in the RSC bundle, which does not extract CSS. Importing a CSS file only from\na Server Component does not produce a browser stylesheet."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Recommended pattern:")," Use class names from a globally loaded stylesheet (Tailwind utilities, global\nCSS, or design tokens imported in the client pack):"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/ProductSummary.tsx (Server Component)</span>\n<span class="token keyword">type</span> <span class="token class-name">Product</span> <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> description<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">ProductSummary</span><span class="token punctuation">(</span><span class="token punctuation">{</span> product <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> product<span class="token operator">:</span> Product <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token punctuation">(</span>\n    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>product-summary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>product<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>product<span class="token punctuation">.</span>description<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token plain-text">\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* app/javascript/styles/application.css — imported by client-bundle.ts */</span>\n<span class="token selector">.product-summary</span> <span class="token punctuation">{</span>\n  <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>\n  <span class="token property">gap</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"The class name is server-rendered by the RSC component. The CSS loads from the Rails layout's\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}}),"."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"CSS Modules in Server Components")," are a special case. The server and RSC bundles process CSS Modules\nwith ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals</code>'}}),", which means the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">import styles from './Foo.module.css'</code>"}})," statement works and\nreturns the class name mapping. The server renders the hashed class names. However, the actual CSS rules\nare only extracted by the client bundle, so the component's CSS file must also be imported somewhere\nin the client graph (typically by a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component that uses the same module, or by\nincluding it in the global stylesheet)."),"\n",s.createElement(n.h3,{id:"client-components-inside-an-rsc-tree",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#client-components-inside-an-rsc-tree","aria-label":"client components inside an rsc tree permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Client Components inside an RSC tree"),"\n",s.createElement(n.p,null,"Put CSS imports behind the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," boundary. This keeps the CSS in the client graph, where it is\nextracted into a file and recorded in the RSC manifest:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/FavoriteButton.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> styles <span class="token keyword">from</span> <span class="token string">\'./FavoriteButton.module.scss\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">FavoriteButton</span><span class="token punctuation">(</span><span class="token punctuation">{</span> active <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> active<span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token punctuation">(</span>\n    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>active <span class="token operator">?</span> styles<span class="token punctuation">.</span>activeButton <span class="token operator">:</span> styles<span class="token punctuation">.</span>button<span class="token punctuation">}</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">\n      Favorite\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/ProductPage.tsx (Server Component)</span>\n<span class="token keyword">import</span> FavoriteButton <span class="token keyword">from</span> <span class="token string">\'./FavoriteButton\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">ProductPage</span><span class="token punctuation">(</span><span class="token punctuation">{</span> product <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> product<span class="token operator">:</span> Product <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token punctuation">(</span>\n    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>product<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">FavoriteButton</span></span> <span class="token attr-name">active</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>product<span class="token punctuation">.</span>favorite<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"The RSC bundle turns ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">FavoriteButton</code>'}})," into a client reference. The client build extracts the SCSS\nModule CSS, the RSC manifest records the CSS href, and the RSC stream injects ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags."),"\n",s.createElement(n.h3,{id:"shared-components",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#shared-components","aria-label":"shared components permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Shared components"),"\n",s.createElement(n.p,null,"A module can be imported as a Server Component in one path and as part of the client graph in another.\nReact's ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directive marks a module dependency subtree, not a render-tree subtree."),"\n",s.createElement(n.p,null,"Guidelines:"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,"Use global classes from a layout-loaded stylesheet when the component renders as a Server Component."),"\n",s.createElement(n.li,null,"Import CSS Modules from a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," wrapper when the component needs scoped styles and renders\nas a Client Component."),"\n",s.createElement(n.li,null,"Avoid CSS side effects in shared utility modules. They make it unclear whether CSS is emitted by the\nclient bundle, ignored by the server/RSC bundle, or duplicated across packs."),"\n"),"\n",s.createElement(n.h2,{id:"css-approaches-in-detail",style:{position:"relative"}},s.createElement(n.a,{href:"#css-approaches-in-detail","aria-label":"css approaches in detail permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"CSS approaches in detail"),"\n",s.createElement(n.h3,{id:"global-css",style:{position:"relative"}},s.createElement(n.a,{href:"#global-css","aria-label":"global css permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Global CSS"),"\n",s.createElement(n.p,null,"Import global CSS from the client pack entry point. The stylesheet loads from the Rails layout\nregardless of rendering mode."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token comment">// app/javascript/packs/client-bundle.ts</span>\n<span class="token keyword">import</span> <span class="token string">\'../styles/application.css\'</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="erb"><pre class="language-erb"><code class="language-erb"><span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> stylesheet_pack_tag <span class="token string-literal"><span class="token string">"client-bundle"</span></span><span class="token punctuation">,</span> <span class="token symbol">media</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">
1"all"</span></span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Use class names freely. CSS loads from the layout.\n",s.createElement(n.strong,null,"Client Components:")," Works. CSS is part of the client bundle.\n",s.createElement(n.strong,null,"Traditional SSR:")," Works when ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}})," is in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),".\n",s.createElement(n.strong,null,"Limitations:")," Not component-scoped. Ordering depends on import order and layout tag placement.\n",s.createElement(n.strong,null,"Status:")," Verified."),"\n",s.createElement(n.h3,{id:"css-modules",style:{position:"relative"}},s.createElement(n.a,{href:"#css-modules","aria-label":"css modules permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"CSS Modules"),"\n",s.createElement(n.p,null,"CSS Modules provide component-scoped class names with build-time hashing. They are the recommended\napproach for scoped styling in React on Rails Pro RSC apps."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/Card.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> styles <span class="token keyword">from</span> <span class="token string">\'./Card.module.css\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Card</span><span class="token punctuation">(</span><span class="token punctuation">{</span> title <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>styles<span class="token punctuation">.</span>card<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>title<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* app/javascript/components/Card.module.css */</span>\n<span class="token selector">.card</span> <span class="token punctuation">{</span>\n  <span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>\n  <span class="token property">
1border</span><span class="token punctuation">:</span> 1px solid #e5e7eb<span class="token punctuation">;</span>\n  <span class="token property">border-radius</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"How it works across bundles:")),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Client bundle:")," ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css-loader</code>'}})," processes the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.module.css</code>'}})," file with CSS Modules mode, generating\nhashed class names (e.g., ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.card</code>'}})," becomes ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.K8av1vsiP9K1YYs501EV</code>'}}),"). ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">MiniCssExtractPlugin</code>'}})," extracts\nthe CSS rules into the output stylesheet. The JavaScript module exports the mapping\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">{ card: 'K8av1vsiP9K1YYs501EV' }</code>"}}),"."),"\n"),"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server bundle:")," ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css-loader</code>'}})," runs with ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals: true</code>'}}),". It generates the same class name\nmapping but emits no CSS output. SSR renders the correct hashed class names in the HTML."),"\n"),"\n",s.createElement(n.li,null,"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"RSC bundle:")," Same as the server bundle for Server Component imports. For ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," modules,\nthe RSC loader replaces the module with a client reference, so the CSS Module import is not\nevaluated in the RSC bundle."),"\n"),"\n"),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Can import CSS Modules and render hashed class names. The CSS rules must also be\navailable in the client bundle (via a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component or global import).\n",s.createElement(n.strong,null,"Client Components:")," Full support. CSS is extracted and recorded in the RSC manifest.\n",s.createElement(n.strong,null,"Traditional SSR:")," Full support. Server renders class names; client stylesheet provides CSS.\n",s.createElement(n.strong,null,"FOUC prevention:")," Yes, via manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags when behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),".\n",s.createElement(n.strong,null,"Status:")," Verified by Pro dummy app specs."),"\n",s.createElement(n.h3,{id:"sassscss",style:{position:"relative"}},s.createElement(n.a,{href:"#sassscss","aria-label":"sassscss permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Sass/SCSS"),"\n",s.createElement(n.p,null,"SCSS Modules work identically to CSS Modules. ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass-loader</code>'}})," compiles SCSS to CSS before ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css-loader</code>'}}),"\nprocesses it. The same ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals</code>'}})," behavior applies in server/RSC bundles."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/FavoriteButton.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">
1import</span> styles <span class="token keyword">from</span> <span class="token string">\'./FavoriteButton.module.scss\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">FavoriteButton</span><span class="token punctuation">(</span><span class="token punctuation">{</span> active <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> active<span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token punctuation">(</span>\n    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>active <span class="token operator">?</span> styles<span class="token punctuation">.</span>activeButton <span class="token operator">:</span> styles<span class="token punctuation">.</span>button<span class="token punctuation">}</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">\n      Favorite\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Required packages:")," ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass-loader</code>'}}),", configured via Shakapacker's default rules.\n",s.createElement(n.strong,null,"Status:")," Verified for SCSS Modules in RSC client boundary."),"\n",s.createElement(n.p,null,"Plain (non-module) SCSS files follow the same rules as plain CSS: import from the client pack for\nglobal styles, or from a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component for scoped usage."),"\n",s.createElement(n.h3,{id:"tailwind-css",style:{position:"relative"}},s.createElement(n.a,{href:"#tailwind-css","aria-label":"tailwind css permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Tailwind CSS"),"\n",s.createElement(n.p,null,"Tailwind CSS is a PostCSS plugin that generates utility CSS at build time. It scans source files for\nclass names and emits only the CSS needed. Since it produces static CSS, it works seamlessly with the\nthree-bundle architecture."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"How Tailwind works with RSC:")),"\n",s.createElement(n.ol,null,"\n",s.createElement(n.li,null,"Tailwind/PostCSS follows each bundle's CSS loader pipeline when CSS imports are processed, but\nonly the client bundle emits browser-loadable CSS."),"\n",s.createElement(n.li,null,"Tailwind scans configured source roots for utility class 
1names. Tailwind v4 uses CSS ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@source</code>'}}),"\ndirectives; Tailwind v3 uses the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">content</code>'}})," array."),"\n",s.createElement(n.li,null,"The generated CSS is delivered through a layout-owned client pack."),"\n",s.createElement(n.li,null,"Server Components and Client Components both use Tailwind class names as plain strings."),"\n",s.createElement(n.li,null,"The CSS loads according to the Rails layout's Shakapacker pack tags."),"\n"),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Critical configuration:")," Tailwind must scan every directory that contains utility class names,\nincluding React component files and ERB views. Tailwind v4 configures those roots from CSS\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@source</code>'}})," directives; Tailwind v3 uses the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">content</code>'}})," array."),"\n",s.createElement(n.h4,{id:"tailwind-css-v4-new-apps",style:{position:"relative"}},s.createElement(n.a,{href:"#tailwind-css-v4-new-apps","aria-label":"tailwind css v4 new apps permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Tailwind CSS v4 (new apps)"),"\n",s.createElement(n.p,null,"The React on Rails generator supports Tailwind v4 via ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">--tailwind</code>'}}),". Tailwind v4 uses a CSS-first\nconfiguration model:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* app/javascript/stylesheets/application.css */</span>\n<span class="token atrule"><span class="token rule">@import</span> <span class="token string">"tailwindcss"</span> <span class="token function">source</span><span class="token punctuation">(</span><span class="token string">"../.."</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// app/javascript/packs/react_on_rails_tailwind.js</span>\n<span class="token keyword">import</span> <span class="token string">\'../stylesheets/application.css\'</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"The generated React on Rails layout declares that pack from the layout:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="erb"><pre class="language-erb"><code class="language-erb"><span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%</span><span class="token ruby language-ruby"> prepend_javascript_pack_tag <span class="token string-literal"><span class="token string">"react_on_rails_tailwind"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n<span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> stylesheet_pack_tag <span class="token string-literal"><span class="token string">"react_on_rails_tailwind"</span></span><span class="token punctuation">,</span> <span class="token symbol">media</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">
1"all"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n<span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> javascript_pack_tag </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"Tailwind v4 uses CSS-level source discovery. The generated stylesheet points at the Rails ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">app/</code>'}}),"\ndirectory by default so Tailwind scans app source without scanning the whole repository, build\noutput, logs, or runtime directories. If your components live outside that source tree, add\nadditional Tailwind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@source</code>'}})," lines to the stylesheet."),"\n",s.createElement(n.h4,{id:"tailwind-css-v3-existing-apps",style:{position:"relative"}},s.createElement(n.a,{href:"#tailwind-css-v3-existing-apps","aria-label":"tailwind css v3 existing apps permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Tailwind CSS v3 (existing apps)"),"\n",s.createElement(n.p,null,"Tailwind v3 requires explicit ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">content</code>'}})," paths. ",s.createElement(n.strong,null,"Include both Rails views and JavaScript component\ndirectories:")),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// config/tailwind.config.js</span>\nmodule<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>\n  <span class="token literal-property property">content</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">\'./app/views/**/*.{erb,haml,slim}\'</span><span class="token punctuation">,</span> <span class="token string">\'./app/javascript/**/*.{js,jsx,ts,tsx}\'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">theme</span><span class="token operator">:</span> <span class="token punctuation">{</span>\n    <span class="token literal-property property">extend</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>\n  <span class="token punctuation">}</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",s.createElement(n.div,{className:"markdown-alert markdown-alert-warning",dir:"auto"},"\n",s.createElement(n.p,{className:"markdown-alert-title",dir:"auto"},s.createElement(n.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},s.createElement(n.path,{d:"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"})),"WARNING"),"\n",s.createElement(n.p,null,"If the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">content</code>'}})," array does not include your React component directory, Tailwind will silently\ndrop any utility classes used only in React components. The classes will appear in source code\nbut have no effect — there will be no build error, just unstyled elements."),"\n"),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Use Tailwind class 
1names freely. CSS loads from the layout.\n",s.createElement(n.strong,null,"Client Components:")," Use Tailwind class names freely. CSS loads from the layout.\n",s.createElement(n.strong,null,"Traditional SSR:")," Works when the Tailwind stylesheet is in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),".\n",s.createElement(n.strong,null,"FOUC prevention:")," Via Rails layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tag (global CSS path).\n",s.createElement(n.strong,null,"Limitations:")," Dynamic class names (template literals, string concatenation) must be statically\ndiscoverable by Tailwind's scanner or explicitly safelisted.\n",s.createElement(n.strong,null,"Status:")," Verified by build analysis; dummy app uses Tailwind v3 globally."),"\n",s.createElement(n.h3,{id:"inline-styles",style:{position:"relative"}},s.createElement(n.a,{href:"#inline-styles","aria-label":"inline styles permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Inline styles"),"\n",s.createElement(n.p,null,"React inline styles (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">style</code>'}})," prop) work everywhere because they are serialized directly in the HTML\nor RSC payload. No external CSS file is needed."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// Works in Server Components, Client Components, and SSR</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Badge</span><span class="token punctuation">(</span><span class="token punctuation">{</span> color <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> color<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token punctuation">(</span>\n    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">style</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> backgroundColor<span class="token operator">:</span> color<span class="token punctuation">,</span> padding<span class="token operator">:</span> <span class="token string">\'0.25rem 0.5rem\'</span><span class="token punctuation">,</span> borderRadius<span class="token operator">:</span> <span class="token string">\'0.25rem\'</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">New</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Works. Style objects are serialized in the RSC Flight payload.\n",s.createElement(n.strong,null,"Client Components:")," Works.\n",s.createElement(n.strong,null,"Traditional SSR:")," Works.\n",s.createElement(n.strong,null,"FOUC prevention:")," Not needed — styles are inline in the HTML.\n",s.createElement(n.strong,null,"Limitations:")," No pseudo-classe
1s, media queries, or keyframe animations. Not ideal for complex\nstyling. Can increase HTML payload size.\n",s.createElement(n.strong,null,"Status:")," Verified by build analysis."),"\n",s.createElement(n.h3,{id:"vanilla-extract",style:{position:"relative"}},s.createElement(n.a,{href:"#vanilla-extract","aria-label":"vanilla extract permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Vanilla Extract"),"\n",s.createElement(n.p,null,s.createElement(n.a,{href:"https://vanilla-extract.style/"},"Vanilla Extract")," compiles TypeScript style definitions to static CSS\nat build time. Since it produces extracted CSS files, it integrates with the RSC manifest pipeline."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Setup:")," Add the Vanilla Extract webpack plugin to your client webpack config. Do not add it to the\nserver or RSC configs — those bundles should not extract CSS."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// config/webpack/clientWebpackConfig.js (append to existing configureClient)</span>\n<span class="token keyword">const</span> <span class="token punctuation">{</span> VanillaExtractPlugin <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">\'@vanilla-extract/webpack-plugin\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token keyword">const</span> MiniCssExtractPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">\'mini-css-extract-plugin\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n<span class="token keyword">const</span> vanillaExtractCssRule <span class="token operator">=</span> <span class="token punctuation">{</span>\n  <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\\.vanilla\\.css$</span><span class="token regex-delimiter">/</span><span class="token regex-flags">i</span></span><span class="token punctuation">,</span>\n  <span class="token literal-property property">use</span><span class="token operator">:</span> <span class="token punctuation">[</span>MiniCssExtractPlugin<span class="token punctuation">.</span>loader<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">loader</span><span class="token operator">:</span> require<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">\'css-loader\'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token literal-property property">options</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// Exclude .vanilla.css from the broad CSS rule to avoid double processing</span>\n<span class="token keyword">const</span> <span class="token function-variable function">excludeVanillaExtractCss</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">rule</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>
1rule <span class="token operator">||</span> <span class="token keyword">typeof</span> rule <span class="token operator">!==</span> <span class="token string">\'object\'</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>\n  <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>rule<span class="token punctuation">.</span>oneOf<span class="token punctuation">)</span><span class="token punctuation">)</span> rule<span class="token punctuation">.</span>oneOf<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>excludeVanillaExtractCss<span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">if</span> <span class="token punctuation">(</span>rule<span class="token punctuation">.</span>test <span class="token keyword">instanceof</span> <span class="token class-name">RegExp</span> <span class="token operator">&amp;&amp;</span> rule<span class="token punctuation">.</span>test<span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span><span class="token string">\'app.css\'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n    rule<span class="token punctuation">.</span>exclude <span class="token operator">=</span> <span class="token punctuation">[</span>
1rule<span class="token punctuation">.</span>exclude<span class="token punctuation">,</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\\.vanilla\\.css$</span><span class="token regex-delimiter">/</span><span class="token regex-flags">i</span></span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">flat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>Boolean<span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token punctuation">}</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">const</span> <span class="token function-variable function">applyVanillaExtract</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">clientConfig</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n  clientConfig<span class="token punctuation">.</span>plugins<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">VanillaExtractPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  clientConfig<span class="token punctuation">.</span>module<span class="token punctuation">.</span>rules<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>excludeVanillaExtractCss<span class="token punctuation">)</span><span class="token punctuation">;</span>\n  clientConfig<span class="token punctuation">.</span>module<span class="token punctuation">.</span>rules<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>vanillaExtractCssRule<span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Usage pattern:")," Keep Vanilla Extract imports behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," for RSC apps:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token comment">// app/javascript/components/productCard.css.ts</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> style <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'@vanilla-extract/css\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">const</span> card <span class="token operator">=</span> <span class="token function">style</span><span class="token punctuation">(</span><span class="token punctuation">{</span>\n  display<span class="token operator">:</span> <span class="token string">\'grid\'</span><span class="token punctuation">,</span>\n  gap<span class="token operator">:</span> <span class="token string">\'0.75rem\'</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/ProductCard.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">
1import</span> <span class="token punctuation">{</span> card <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'./productCard.css\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">ProductCard</span><span class="token punctuation">(</span><span class="token punctuation">{</span> product <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> product<span class="token operator">:</span> Product <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>card<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>product<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"The import specifier uses ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">productCard.css</code>'}})," (no ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.ts</code>'}}),"). Vanilla Extract's bundler plugin resolves the\nauthored ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.css.ts</code>'}})," module and emits ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.vanilla.css</code>'}}),". The broad CSS rule must exclude ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.vanilla.css</code>'}}),"\nso the custom rule handles it."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Importing ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.css.ts</code>'}})," directly from a Server Component requires additional\nserver/RSC bundle configuration. Use the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," wrapper pattern instead.\n",s.createElement(n.strong,null,"Client Components:")," Works when the build plugin and CSS extraction rules are configured.\n",s.createElement(n.strong,null,"FOUC prevention:")," Yes, via manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags when behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),".\n",s.createElement(n.strong,null,"Limitations:")," Not verified end-to-e
1nd with React on Rails Pro RSC. The ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.css.ts</code>'}})," import may need\nan ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">swc-plugin-vanilla-extract</code>'}})," workaround in some setups. Inspect your ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}}),"\nto confirm CSS appears.\n",s.createElement(n.strong,null,"Status:")," Assumed from build-tool behavior. Not covered by a Pro regression test."),"\n",s.createElement(n.h3,{id:"styled-components",style:{position:"relative"}},s.createElement(n.a,{href:"#styled-components","aria-label":"styled components permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"styled-components"),"\n",s.createElement(n.p,null,"styled-components is a runtime CSS-in-JS library. It generates CSS at runtime by injecting ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;style></code>'}}),"\ntags into the DOM. This means its CSS is ",s.createElement(n.strong,null,"not")," extracted into files and ",s.createElement(n.strong,null,"not")," recorded in\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}}),"."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/StyledButton.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> styled <span class="token keyword">from</span> <span class="token string">\'styled-components\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">const</span> Button <span class="token operator">=</span> styled<span class="token punctuation">.</span>button<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">\n  background-color: peachpuff;\n  padding: 0.5rem 1rem;\n  border: none;\n  border-radius: 0.25rem;\n  cursor: pointer;\n</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">StyledButton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Button</span></span><span class="token punctuation">></span></span><span class="token plain-text">Click me</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Button</span></span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.div,{className:"markdown-alert markdown-alert-important",dir:"auto"},"\n",s.createElement(n.p,{className:"markdown-alert-title",dir:"auto"},s.createElement(n.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},s.createElement(n.path,{d:"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"})),"IMPORTANT"),"\n",s.createElement(n.p,null,"styled-components ",s.createElement(n.strong,null,"must")," be used behind a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," boundary. Using it in a Server Component\nwill crash because it depends on React Context and ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useRef</code>'}}),"."),"\n"),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Not supported. Will throw runtime errors.\n",s.createElement(n.strong,null,"Client Components:")," Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),". styled-components v6 includes React 19\ncompatibility fixes.\n",s.createElement(n.strong,null,"Traditional SSR:")," Works with ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ServerStyleSheet</code>'}})," for style extraction during SSR. Requires\napp-specific integration with the node renderer.\n",s.createElement(n.strong,null,"FOUC prevention:")," None from the RSC manifest pipeline. Runtime-injected styles load after\nJavaScript executes, which can cause a flash of unstyled content on RSC pages.\n",s.createElement(n.strong,null,"Limitations:")),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,"Context-based theming (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ThemeProvider</code>'}}),") is not available in Server Components. Use CSS custom\nproperties for cross-boundary theming."),"\n",s.createElement(n.li,null,'styled-components is in maintenance mode. The maintainer has stated: "For new projects, I would not\nrecommend adopting styled-components."'),"\n",s.createElement(n.li,null,"SSR style collection requires ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ServerStyleSheet</code>'}})," wrapping, which is not built into React on Rails\nPro's node renderer by default.\n",s.createElement(n.strong,null,"Status:")," Unknown for React on Rails Pro RSC integration. Works in client-only usage."),"\n"),"\n",s.createElement(n.h3,{id:"emotion",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#emotion","aria-label":"emotion permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Emotion"),"\n",s.createElement(n.p,null,"Emotion is a runtime CSS-in-JS library similar to styled-components. The same architectural\nconstraints apply: runtime ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;style></code>'}})," injection, no extracted CSS chunk recording, no RSC FOUC prevention."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// app/javascript/components/EmotionCard.tsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> styled <span class="token keyword">from</span> <span class="token string">\'@emotion/styled\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">const</span> Card <span class="token operator">=</span> styled<span class="token punctuation">.</span>div<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">\n  background-color: powderblue;\n  padding: 1rem;\n  border-radius: 0.5rem;\n</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">EmotionCard</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Card</span></span><span class="token punctuation">></span></span><span class="token plain-text">Emotion-styled card</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Card</span></span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Server Components:")," Not supported. Emotion depends on React Context (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">CacheProvider</code>'}}),").\n",s.createElement(n.strong,null,"Client Components:")," Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),".\n",s.createElement(n.strong,null,"Traditional SSR:")," Works with Emotion's SSR cache/extraction setup (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@emotion/server</code>'}}),",\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">extractCriticalToChunks</code>'}}),"). Requires app-specific integration.\n",s.createElement(n.strong,null,"FOUC prevention:")," None from the RSC manifest pipeline.\n",s.createElement(n.strong,null,"Status:")," Unknown for RSC. Assumed to work for client-only usage."),"\n",s.createElement(n.h3,{id:"other-static-extraction-libraries",style:{position:"relative"}},s.createElement(n.a,{href:"#other-static-extraction-libraries","aria-label":"other static extraction libraries permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Other static extraction libraries"),"\n",s.createElement(n.p,null,"Libraries like ",s.createElement(n.a,{href:"https://linaria.dev/"},"Linaria"),", ",s.createElement(n.a,{href:"https://panda-css.com/"},"Panda CSS"),",\n",s.createElement(n.a,{href:"https://stylexjs.com/"},"StyleX"),", and ",s.createElement(n.a,{href:"https://compiledcssinjs.com/"},"Compiled")," extract CSS at build\ntime, producing static CSS files that Shakapacker can serve."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"General principle:")," If the library produces a CSS file that can be imported from a client pack or\na ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component, it will work with React on Rails Pro's RSC architecture. The CSS enters\nthe client bundle and is extracted normally."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Setup pattern:")),"\n",s.createElement(n.ol,null,"\n",s.createElement(n.li,null,"Add the library's bundler plugin to your ",s.createElement(n.strong,null,"client webpack/Rspack config only"),"."),"\n",s.createElement(n.li,null,"Import the library's generated CSS from a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component or the global stylesheet."),"\n",s.createElement(n.li,null,"Verify the extracted CSS appears in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}})," if using the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," path."),"\n",s.createElement(n.li,null,"Do not add the library's plugin to the server or RSC bundle configs unless the library specifically\nrequires it for class name resolution (check the library's RSC documentation)."),"\n"),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Status:")," Assumed. None of these libraries are covered by React on Rails Pro regression tests."),"\n",s.createElement(n.h3,{id:"class-name-utilities-clsx-classnames-cva",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#class-name-utilities-clsx-classnames-cva","aria-label":"class name utilities clsx classnames cva permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Class name utilities (clsx, classnames, CVA)"),"\n",s.createElement(n.p,null,"These libraries compose class name strings at runtime. They do not emit CSS themselves."),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token keyword">import</span> clsx <span class="token keyword">from</span> <span class="token string">
1\'clsx\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Alert</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">\'info\'</span> <span class="token operator">|</span> <span class="token string">\'error\'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token function">clsx</span><span class="token punctuation">(</span><span class="token string">\'alert\'</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">alert-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">type</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"They work everywhere — Server Components, Client Components, SSR — because they are pure functions\nthat return strings. Pair them with a CSS approach that provides the actual class definitions\n(Tailwind, CSS Modules, global CSS)."),"\n",s.createElement(n.p,null,s.createElement(n.strong,null,"Status:")," Assumed; low risk."),"\n",s.createElement(n.h2,{id:"react-on-rails-asset-rendering",style:{position:"relative"}},s.createElement(n.a,{href:"#react-on-rails-asset-rendering","aria-label":"react on rails asset rendering permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"React on Rails asset rendering"),"\n",s.createElement(n.h3,{id:"manual-pack-loading",style:{position:"relative"}},s.createElement(n.a,{href:"#manual-pack-loading","aria-label":"manual pack loading permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Manual pack loading"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="erb"><pre class="language-erb"><code class="language-erb"><span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby">
1 stylesheet_pack_tag <span class="token string-literal"><span class="token string">"client-bundle"</span></span><span class="token punctuation">,</span> <span class="token symbol">media</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"all"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n<span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> javascript_pack_tag <span class="token string-literal"><span class="token string">"client-bundle"</span></span><span class="token punctuation">,</span> <span class="token symbol">defer</span><span class="token operator">:</span> <span class="token boolean">true</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",s.createElement(n.h3,{id:"auto-loaded-component-packs",style:{position:"relative"}},s.createElement(n.a,{href:"#auto-loaded-component-packs","aria-label":"auto loaded component packs permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Auto-loaded component packs"),"\n",s.createElement(n.p,null,"With ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle: true</code>'}}),", use argless tag placeholders. React on Rails appends component pack\nnames during rendering:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="erb"><pre class="language-erb"><code class="language-erb"><span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> stylesheet_pack_tag media<span class="token operator">:</span> <span class="token string-literal"><span class="token string">"all"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n<span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> javascript_pack_tag defer<span class="token operator">:</span> <span class="token boolean">true</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"When using SSR with ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle</code>'}}),", render the body before the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," so the component pack\nnames are available when the stylesheet tags are emitted:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="erb"><pre class="language-erb"><code class="language-erb"><span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%</span><span class="token ruby language-ruby"> content_for <span class="token symbol">:body_content</span> <span class="token keyword">do</span> </span><span class="token delimiter punctuation">%></span></span>\n  <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> <span class="token keyword">yield</span> </span><span class="token delimiter punctuation">%></span></span>\n<span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%</span><span class="token ruby language-ruby"> <span class="token keyword">end</span> </span><span class="token delimiter punctuation">%></span></span>\n\n<span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span>\n  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> csrf_meta_tags </span><span class="token delimiter punctuation">%></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> csp_meta_tag </span><span class="token delimiter punctuation">%></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby">
1 stylesheet_pack_tag <span class="token string-literal"><span class="token string">"client-bundle"</span></span><span class="token punctuation">,</span> <span class="token symbol">media</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"all"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> stylesheet_pack_tag media<span class="token operator">:</span> <span class="token string-literal"><span class="token string">"all"</span></span> </span><span class="token delimiter punctuation">%></span></span>\n  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span>\n  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> <span class="token keyword">yield</span> <span class="token symbol">:body_content</span> </span><span class="token delimiter punctuation">%></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> javascript_pack_tag <span class="token string-literal"><span class="token string">"client-bundle"</span></span><span class="token punctuation">,</span> <span class="token symbol">defer</span><span class="token operator">:</span> <span class="token boolean">true</span> </span><span class="token delimiter punctuation">%></span></span>\n    <span class="token erb language-erb"><span class="token delimiter punctuation">&lt;%=</span><span class="token ruby language-ruby"> javascript_pack_tag defer<span class="token operator">:</span> <span class="token boolean">true</span> </span><span class="token delimiter punctuation">%></span></span>\n  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div>'}}),"\n",s.createElement(n.h3,{id:"rsc-pages",style:{position:"relative"}},s.createElement(n.a,{href:"#rsc-pages","aria-label":"rsc pages permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"RSC pages"),"\n",s.createElement(n.p,null,"For pages rendered by ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}),", CSS for ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," references is handled by the\nstreamed-CSS pipeline described in ",s.createElement(n.a,{href:"#how-css-reaches-the-browser"},"How CSS reaches the browser"),".\nKeep the Rails stylesheet tags anyway: they carry global CSS, the generated entry-pack CSS that\nstyles the shell at first paint, and CSS for non-RSC components."),"\n",s.createElement(n.h2,{id:"verifying-css-in-production-builds",style:{position:"relative"}},s.createElement(n.a,{href:"#verifying-css-in-production-builds","aria-label":"verifying css in production builds permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Verifying CSS in production builds"),"\n",s.createElement(n.p,null,"Development HMR can hide or introduce FOUC that does not exist in production. Always verify with\nproduction-like builds:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">RAILS_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">NODE_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">CLIENT_BUNDLE_ONLY</span><span class="token operator">=</span>true bin/shakapacker\n<span class="token assign-left variable">RAILS_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">NODE_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">SERVER_BUNDLE_ONLY</span><span class="token operator">=</span>true bin/shakapacker\n<span class="token assign-left variable">RAILS_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">NODE_ENV</span><span class="token operator">=</span>production <span class="token assign-left variable">RSC_BUNDLE_ONLY</span><span class="token operator">=</span>true bin/shakapacker</code></pre></div>'}}),"\n",s.createElement(n.p,null,"Then inspect:"),"\n",s.createElement(n.ol,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">public/&lt;public_output_path>/manifest.json</code>'}}))," — Shakapacker asset manifest. Check that your CSS\nfiles are listed."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">public/&lt;public_output_path>/react-client-manifest.json</code>'}}))," — RSC client manifest. Check that\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," modules have ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," arrays pointing to the correct stylesheet files."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Server-rendered HTML")," — Look for ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="stylesheet"></code>'}})," tags before the first styled\ncomponent. For RSC pages, look for ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="stylesheet" data-precedence="rsc-css"></code>'}})," tags."),"\n"),"\n",s.createElement(n.h2,{id:"compatibility-matrix",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#compatibility-matrix","aria-label":"compatibility matrix permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Compatibility matrix"),"\n",s.createElement(n.p,null,"Status key:"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Verified"),": covered by current repo code, docs, or build analysis."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Assumed"),": expected from current architecture and package behavior, but not covered by a\nReact on Rails Pro regression fixture."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Unsupported"),": does not fit the current generated RSC/server CSS pipeline."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Unknown"),": needs a fixture or package-specific investigation before recommendation."),"\n"),"\n",s.createElement(n.table,null,s.createElement(n.thead,null,s.createElement(n.tr,null,s.createElement(n.th,null,"Approach"),s.createElement(n.th,null,"Server Component"),s.createElement(n.th,null,"Client Component (RSC)"),s.createElement(n.th,null,"Traditional SSR"),s.createElement(n.th,null,"FOUC prevention"),s.createElement(n.th,null,"Required config"),s.createElement(n.th,null,"Status"))),s.createElement(n.tbody,null,s.createElement(n.tr,null,s.createElement(n.td,null,"Global CSS (layout pack)"),s.createElement(n.td,null,"Works (class names render; CSS loads globally)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Rails ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})),s.createElement(n.td,null,"Import CSS in client pack; ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}})," in layout"),s.createElement(n.td,null,"Verified")),s.createElement(n.tr,null,s.createElement(n.td,null,"CSS Modules (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),")"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals</code>'}})," renders class names"),s.createElement(n.td,null,"Works; CSS extracted and in manifest"),s.createElement(n.td,null,"Works; server renders locals"),s.createElement(n.td,null,"Manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags"),s.createElement(n.td,null,"Shakapacker CSS Modules config; RSC manifest plugin"),s.createElement(n.td,null,"Verified")),s.createElement(n.tr,null,s.createElement(n.td,null,"CSS Modules (Server Component only)"),s.createElement(n.td,null,"Class names render but CSS is not emitted"),s.createElement(n.td,null,"N/A"),s.createElement(n.td,null,"Class names render but CSS not emitted"),s.createElement(n.td,null,"None"),s.createElement(n.td,null,"Move CSS to client graph"),s.createElement(n.td,null,"Unsupported")),s.createElement(n.tr,null,s.createElement(n.td,null,"SCSS Modules (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),")"),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"Same as CSS Modules"),s.createElement(n.td,null,"Manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sass-loader</code>'}})),s.createElement(n.td,null,"Verified")),s.createElement(n.tr,null,s.createElement(n.td,null,"Tailwind CSS"),s.createElement(n.td,null,"Works (utility class 
1names)"),s.createElement(n.td,null,"Works (utility class names)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Rails ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})),s.createElement(n.td,null,"PostCSS config; content paths must include component dirs"),s.createElement(n.td,null,"Verified (build)")),s.createElement(n.tr,null,s.createElement(n.td,null,"Inline styles"),s.createElement(n.td,null,"Works (serialized in RSC payload)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"N/A"),s.createElement(n.td,null,"None"),s.createElement(n.td,null,"Verified (build)")),s.createElement(n.tr,null,s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clsx</code>'}}),"/",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">classnames</code>'}}),"/CVA"),s.createElement(n.td,null,"Works (pure string functions)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"N/A"),s.createElement(n.td,null,"None; pair with a CSS source"),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"Vanilla Extract"),s.createElement(n.td,null,"Needs ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," wrapper"),s.createElement(n.td,null,"Works with build plugin"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})," tags"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@vanilla-extract/webpack-plugin</code>'}})," in client config"),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"Linaria"),s.createElement(n.td,null,"Needs ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," wrapper"),s.createElement(n.td,null,"Works after Babel/loader setup"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Depends on import path"),s.createElement(n.td,null,"WyW Babel preset; ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@wyw-in-js/webpack-loader</code>'}})),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"Panda CSS"),s.createElement(n.td,null,"Works (classes are static strings)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Rails ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})),s.createElement(n.td,null,"Panda CLI or PostCSS; import generated CSS in layout"),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"StyleX"),s.createElement(n.td,null,"Works (classes are static strings)"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Rails ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link></code>'}})),s.createElement(n.td,null,"StyleX Babel plugin; import generated CSS"),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"Compiled"),s.createElement(n.td,null,"Needs ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," wrapper"),s.createElement(n.td,null,"Works with webpack loader"),s.createElement(n.td,null,"Works"),s.createElement(n.td,null,"Depends on import path"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@compiled/webpack-loader</code>'}}),"; CSS extraction"),s.createElement(n.td,null,"Assumed")),s.createElement(n.tr,null,s.createElement(n.td,null,"styled-components v6"),s.createElement(n.td,null,s.createElement(n.strong,null,"Not supported")," (crashes)"),s.createElement(n.td,null,"Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})),s.createElement(n.td,null,"Works with ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ServerStyleSheet</code>'}})),s.createElement(n.td,null,s.createElement(n.strong,null,"None")," (runtime injection)"),s.createElement(n.td,null,"Recent v6; optional Babel/SWC plugin"),s.createElement(n.td,null,"Unknown for Pro RSC")),s.createElement(n.tr,null,s.createElement(n.td,null,"Emotion"),s.createElement(n.td,null,s.createElement(n.strong,null,"Not supported")," (crashes)"),s.createElement(n.td,null,"Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})),s.createElement(n.td,null,"Works with SSR cache"),s.createElement(n.td,null,s.createElement(n.strong,null,"None")," (runtime injection)"),s.createElement(n.td,null,s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@emotion/react</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@emotion/styled</code>'}}),"; SSR setup"),s.createElement(n.td,null,"Unknown for RSC")),s.createElement(n.tr,null,s.createElement(n.td,null,"Runtime component libraries"),s.createElement(n.td,null,"Treat as Client Components"),s.createElement(n.td,null,"Works behind ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})),s.createElement(n.td,null,"Depends on library SSR support"),s.createElement(n.td,null,s.createElement(n.strong,null,"None")," usually"),s.createElement(n.td,null,"Library-specific"),s.createElement(n.td,null,"Unknown")))),"\n",s.createElement(n.h2,{id:"common-pitfalls",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#common-pitfalls","aria-label":"common pitfalls permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Common pitfalls"),"\n",s.createElement(n.h3,{id:"importing-css-only-from-a-server-component",style:{position:"relative"}},s.createElement(n.a,{href:"#importing-css-only-from-a-server-component","aria-label":"importing css only from a server component permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Importing CSS only from a Server Component"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token comment">// WRONG: CSS is not emitted to the browser</span>\n<span class="token keyword">import</span> <span class="token string">\'./ProductSummary.css\'</span><span class="token punctuation">;</span> <span class="token comment">// only imported here, a Server Component</span>\n\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">ProductSummary</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>product-summary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"The server renders ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;div class="product-summary"></code>'}}),", but no stylesheet is loaded. The element appears\nunstyled. Move the CSS import to the client pack or a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component."),"\n",s.createElement(n.h3,{id:"missing-component-directories-in-tailwind-content-paths",style:{position:"relative"}},s.createElement(n.a,{href:"#missing-component-directories-in-tailwind-content-paths","aria-label":"missing component directories in tailwind content paths permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Missing component directories in Tailwind content paths"),"\n",s.createElement(n.p,null,"If Tailwind classes work in ERB views but not in React components, check that the component directory\nis in Tailwind's ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">content</code>'}})," array. Tailwind v3 does not scan files outside its configured paths."),"\n",s.createElement(n.h3,{id:"using-runtime-css-in-js-without-understanding-fouc-implications",style:{position:"relative"}}
1,s.createElement(n.a,{href:"#using-runtime-css-in-js-without-understanding-fouc-implications","aria-label":"using runtime css in js without understanding fouc implications permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Using runtime CSS-in-JS without understanding FOUC implications"),"\n",s.createElement(n.p,null,"styled-components and Emotion work in Client Components, but their CSS loads after JavaScript\nexecutes. On RSC pages, this means a visible flash where the component renders with no styles,\nthen styles appear once JavaScript hydrates. For new components, prefer CSS Modules or Tailwind."),"\n",s.createElement(n.h3,{id:"adding-css-extraction-plugins-to-serverrsc-webpack-configs",style:{position:"relative"}},s.createElement(n.a,{href:"#adding-css-extraction-plugins-to-serverrsc-webpack-configs","aria-label":"adding css extraction plugins to serverrsc webpack configs permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Adding CSS extraction plugins to server/RSC webpack configs"),"\n",s.createElement(n.p,null,"The server and RSC bundles should ",s.createElement(n.strong,null,"not")," have ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">MiniCssExtractPlugin</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">style-loader</code>'}}),", or any CSS\ninjection mechanism. CSS Modules should use ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">exportOnlyLocals: true</code>'}}),". The generated Pro configs\nhandle this correctly — do not override it."),"\n",s.createElement(n.h3,{id:"forgetting-to-rebuild-all-three-bundles-after-css-changes",style:{position:"relative"}},s.createElement(n.a,{href:"#forgetting-to-rebuild-all-three-bundles-after-css-changes","aria-label":"forgetting to rebuild all three bundles after css changes permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Forgetting to rebuild all three bundles after CSS changes"),"\n",s.createElement(n.p,null,"CSS changes that affect the RSC manifest (new ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," components with CSS imports, new CSS\nModule files) require rebuilding all three bundles. The manifest is generated from the client build\nbut 
1consumed by the RSC renderer."),"\n",s.createElement(n.h3,{id:"shakapacker-v9-css-modules-default-change",style:{position:"relative"}},s.createElement(n.a,{href:"#shakapacker-v9-css-modules-default-change","aria-label":"shakapacker v9 css modules default change permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Shakapacker v9 CSS Modules default change"),"\n",s.createElement(n.p,null,"Shakapacker v9 changed CSS Modules defaults to ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">namedExport: true</code>'}})," and\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">exportLocalsConvention: 'camelCaseOnly'</code>"}}),". This breaks code using the default export pattern\n(",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">import styles from './Foo.module.scss'</code>"}}),"). The generated Pro configs override this to preserve\nthe original behavior (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">namedExport: false</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">exportLocalsConvention: 'camelCase'</code>"}}),"). If you\ncustomize your webpack CSS rules, check that the overrides are still in place."),"\n",s.createElement(n.h3,{id:"importing-global-css-in-the-server-bundle-entry-point",style:{position:"relative"}},s.createElement(n.a,{href:"#importing-global-css-in-the-server-bundle-entry-point","aria-label":"importing global css in the server bundle entry point permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Importing global CSS in the server bundle entry point"),"\n",s.createElement(n.p,null,"The server bundle entry (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">server-bundle.js</code>'}}),") should ",s.createElement(n.strong,null,"not")," import ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">application.css</code>'}})," or other\nglobal CSS files. CSS imports in the server bundle resolve to empty modules or class-name-only\nmappings. Importing Tailwind's CSS in the server entry wastes build time without producing usable\noutput."),"\n",s.createElement(n.h3,{id:"rsc-stylesheet-cascade-order-end-of-head-precedence",style:{position:"relative"}},s.createElement(n.a,{href:"#rsc-stylesheet-cascade-order-end-of-head-precedence","aria-label":"rsc stylesheet cascade order end of head precedence permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"RSC stylesheet cascade order (end-of-",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}})," precedence)"),"\n",s.createElement(n.p,null,"This pitfall applies to React 19+ installations, where React manages stylesheet precedence groups\nvia the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence</code>'}})," attribute. React 18 does not hoist these stylesheet groups."),"\n",s.createElement(n.p,null,"The RSC client-chunk stylesheet pipeline emits each CSS href referenced by the current Flight payload\nas a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;link rel="stylesheet" data-precedence="rsc-css"></code>'}})," tag. React places every\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence="rsc-css"</code>'}})," link at the ",s.createElement(n.strong,null,"end")," of ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;head></code>'}}),", after framework and vendor CSS, so when\nspecificity is equal, these stylesheets win source-order ties against precedence-less stylesheets —\nincluding the Rails-layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}})," links that have no ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence</code>'}})," attribute."),"\n",s.createElement(n.p,null,"This matters when an RSC CSS Module contains an ",s.createElement(n.strong,null,"unscoped global selector"),". CSS Module ",s.createElement(n.em,null,"class names"),"\nare scoped, but selectors such as ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">html</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">body</code>'}}),", ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">:root</code>'}}),", the universal selector ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">*</code>'}}),", bare\npseudo-elements like ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">::before</code>'}})," or ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">::placeholder</code>'}}),", and attribute selectors like ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">[data-theme]</code>'}})," still\napply globally. Because the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">data-precedence="rsc-css"</code>'}})," group lands last, an unscoped selector inside a\nCSS Module can override your global styles unexpectedly once that stylesheet is delivered:"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* WRONG: a Bootstrap-style bare element selector inside an RSC CSS Module.\n   It is NOT scoped, and the rsc-css group lands at the end of &lt;head>, so this\n   will win source-order ties against a global html rule and reset the root\n   font-size site-wide. */</span>\n<span class="token selector">html</span> <span class="token punctuation">{</span>\n  <span class="token property">font-size</span><span class="token punctuation">:</span> 14px<span class="token punctuation">;</span>\n<span class="token punctuation">}</span>\n\n<span class="token selector">.card</span> <span class="token punctuation">{</span>\n  <span class="token comment">/* scoped class names are fine */</span>\n  <span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"Defensive-specificity guidance: never put bare element selectors at the top of (or anywhere in) an\nRSC CSS Module — scope every rule to a class. Keep global resets like ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">html { font-size }</code>'}})," in a\ndedicated global stylesheet loaded through the Rails layout, not in a CSS Module that rides along\nwith a ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," reference."),"\n",s.createElement(n.p,null,"For teams that want the strictest no-type-selector convention, including rejecting scoped de
1scendants\nsuch as ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.card a</code>'}}),", add a per-file override so CI catches element selectors in CSS Modules before they\nreach production:"),"\n",s.createElement(n.p,null,"Example ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.stylelintrc.json</code>'}}),":"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>\n  <span class="token property">"overrides"</span><span class="token operator">:</span> <span class="token punctuation">[</span>\n    <span class="token punctuation">{</span>\n      <span class="token property">"files"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"**/*.module.css"</span><span class="token punctuation">,</span> <span class="token string">"**/*.module.scss"</span><span class="token punctuation">,</span> <span class="token string">"**/*.module.sass"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n      <span class="token property">"rules"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"selector-max-type"</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span>\n    <span class="token punctuation">}</span>\n  <span class="token punctuation">]</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"If your CSS Module convention intentionally permits scoped descendants such as ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.card a</code>'}}),", use\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">selector-max-type: [0, { "ignore": ["descendant"] }]</code>'}})," instead — this still blocks bare top-level\nselectors like ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">html</code>'}})," or ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">body</code>'}})," while allowing ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.card a</code>'}}),":"),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>\n  <span class="token property">"overrides"</span><span class="token operator">:</span> <span class="token punctuation">[</span>\n    <span class="token punctuation">{</span>\n      <span class="token property">"files"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"**/*.module.css"</span><span class="token punctuation">,</span> <span class="token string">"**/*.module.scss"</span><span class="token punctuation">,</span> <span class="token string">"**/*.module.sass"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n      <span class="token property">"rules"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"selector-max-type"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token property">"ignore"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"descendant"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span>\n    <span class="token punctuation">}</span>\n  <span class="token punctuation">]</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* ProductCard.module.css -- CORRECT: class-scoped rules only */</span>\n<span class="token selector">.card</span> <span class="token punctuation">{</span>\n  <span class="token property">font-size</span><span class="token punctuation">:</span> 0.875rem<span class="token punctuation">;</span>\n  <span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* application.css -- CORRECT: global reset loaded through the Rails layout */</span>\n<span class="token selector">html</span> <span class="token punctuation">{</span>\n  <span class="token property">font-size</span><span class="token punctuation">:</span> 14px<span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",s.createElement(n.p,null,"See ",s.createElement(n.a,{href:"../../oss/migrating/rsc-troubleshooting.md#rsc-stylesheet-injection-render-blocking-links-and-cascade-order"},"RSC stylesheet injection troubleshooting"),"\nfor the render-blocking, client-chunk-driven injection, and cascade behavior behind these links."),"\n",s.createElement(n.p,null,"See ",s.createElement(n.a,{href:"../../oss/building-features/performance-tracks-and-profiling.md#measuring-an-rsc-conversion-with-a-paired-ab"},"React Performance Tracks and Profiling"),"\nto measure the end-to-e
1nd performance impact of RSC changes with a paired A/B comparison."),"\n",s.createElement(n.h2,{id:"known-limitations",style:{position:"relative"}},s.createElement(n.a,{href:"#known-limitations","aria-label":"known limitations permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"Known limitations"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,"RSC stylesheet links are filtered by client chunk names found in the current Flight payload, not by\nblindly linking every client manifest entry. The links can still include any CSS bundled into those\nreferenced chunks, so broad client boundaries or shared chunks can make more CSS render-blocking than\na single component appears to need."),"\n",s.createElement(n.li,null,"The streamed reveal gate is ",s.createElement(n.strong,null,"fail-open")," (100ms budget) — see\n",s.createElement(n.a,{href:"#when-the-fouc-pipeline-silently-does-nothing"},"When the FOUC pipeline silently does nothing"),"."),"\n",s.createElement(n.li,null,"The same stylesheet href can legitimately appear twice in the stream — see\n",s.createElement(n.a,{href:"#where-the-late-css-comes-from-two-paths-one-bucket"},"two paths, one bucket"),"."),"\n",s.createElement(n.li,null,"The reveal-deferral mechanism reads React's inline ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">$RC(</code>'}})," scripts — see\n",s.createElement(n.a,{href:"#coupling-to-react-internals-what-breaks-where"},"Coupling to React internals")," and re-verify on\nevery React upgrade, including minors."),"\n",s.createElement(n.li,null,"Older ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-client-manifest.json</code>'}})," files without ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," arrays (pre ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">[email protected]</code>'}}),")\ncannot produce RSC stylesheet links. Rebuild all three bundles after upgrading."),"\n",s.createElement(n.li,null,"For client-side RSC navigation (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RSCRoute</code>'}}),"), the RSC payload still needs stylesheet links. Verify\nthis path separately for route-heavy apps."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Production builds are the primary silent-no-op case (both bundlers, verified 2026-07-09):"),"\nin a default ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">NODE_ENV=production</code>'}})," build, chunk ids are numeric (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">optimization.chunkIds:\n'deterministic'</code>"}}),") and extracted chunk CSS is id-named (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css/[id]-[hash].css</code>'}}),"), so Path B's\nFlight regex is disabled. Path A's old ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css/clientN-*.css</code>'}})," filename fallback is disabled too, so\nstreamed reveal gating then depends on the preload href matching\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rscClientManifestStylesheetHrefs</code>'}})," and arriving before the reveal. The reviewed production dummy\ncapture under both bundlers still emitted only a non-blocking preload, so the reveal was ungated\nand only the hint-driven preload's head start mitigated the flash. Note that CSS split into async\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientN</code>'}})," chunks is ",s.createElement(n.strong,null,"not")," covered by the Rails layout ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}})," (layout pack tags\nemit only the entrypoint's initial-chunk CSS); until this gap is closed, the styles for such\ncomponents apply at hydration time. The dev/test posture (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">chunkIds: 'named'</code>"}}
1),"), which is what CI\nexercises, is not affected."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Rspack manifest ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," omission:")," the ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RSCRspackPlugin</code>'}})," collects ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," arrays only on the\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">react-on-rails-rsc</code>'}})," 19.2.1+ line, and omits them (with only a compile warning) when\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">output.publicPath</code>'}})," is ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'auto'</code>"}})," or a function, or when CSS is extracted by something other than\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">CssExtractRspackPlugin</code>'}})," (",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css/mini-extract</code>'}})," module type). Without ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">css</code>'}})," arrays there are no\nstylesheet hints and no preload head start at all. See\n",s.createElement(n.a,{href:"./rspack-compatibility.md#css--fouc-status"},"Rspack compatibility"),"."),"\n",s.createElement(n.li,null,s.createElement(n.strong,null,"Rspack CSS Module class name divergence:")," When using Rspack with CSS Modules, avoid\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">[contenthash]</code>'}})," in ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">localIdentName</code>'}}),". Rspack client and server builds may produce different\ncontent hashes for the same file, causing SSR class name mismatches. Use a stable ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getLocalIdent</code>'}}),"\nfunction based on file path and class name instead. See the\n",s.createElement(n.a,{href:"/linked-assets/333882f02f30b167f7de476e66dda0e8/migrating-from-webpack-to-rspack.md"},"webpack-to-Rspack migration guide"),"."),"\n",s.createElement(n.li,null,"This page does not include regression fixtures for Tailwind, Vanilla Extract, Linaria, Panda CSS,\nCompiled, StyleX, styled-components, Emotion, or component-library styling systems."),"\n"),"\n",s.createElement(n.h2,{id:"see-also",style:{position:"relative"}},s.createElement(n.a,{href:"#see-also","aria-label":"see also permalink",className:"anchor before"},s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}})),"See also"),"\n",s.createElement(n.ul,null,"\n",s.createElement(n.li,null,s.createElement(n.a,{href:"/linked-assets/c58a03c2636d5001fcf3c67398946d1d/rendering-flow.md"},"RSC rendering flow")," — client, server, and RSC bundle lifecycle"),"\n",s.createElement(n.li,null,s.createElement(n.a,{href:"/linked-assets/4e52eb84fec17740ac0a91bc6256f37a/styling-with-tailwind.md"},"Styling with Tailwind CSS")," — generator setup\nfor Tailwind v4"),"\n",s.createElement(n.li,null,s.createElement(n.a,{href:"/linked-assets/558844276b3e427d2b18fedf3857087c/view-helpers-api.md"},"View helpers API")," — ",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}),",\n",s.createElement(n.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stylesheet_pack_tag</code>'}}),", and related helpers"),"\n",s.createElement(n.li,null,s.createElement(n.a,{href:"/linked-assets/acfb09bf7dbe0fddd7b750d34481c9c4/rsc-third-party-libs.md"},"Third-party library compatibility")," — RSC migration\nnotes for CSS-in-JS and other libraries"),"\n",s.createElement(n.li,null,s.createElement(n.a,{href:"/linked-assets/9463940ea7
1d181f44639075db10303a8/rspack-compatibility.md"},"Rspack compatibility")," — bundler compatibility matrix"),"\n"))}n.A=function(e){void 0===e&&(e={});const{wrapper:n}=Object.assign({},(0,t.R)(),e.components);return n?s.createElement(n,e,s.createElement(l,e)):l(e)}}}]);
2//# sourceMappingURL=0a6e3696-70be7353577326f2cde6.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.