PageSourceSearch

https://shakacode.com/component---src-template-doc-article-tsx-con…mponent-patterns-md-9b5c101590dbccd1ccea.js

js shakacode.com collected 2026-10-04 00:27:14 UTC 119,243 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkgatsby_starter_default=self.webpackChunkgatsby_starter_default||[]).push([[2294],{95580:function(n,a,s){s.r(a),s.d(a,{Head:function(){return g},default:function(){return m}});var t=s(28453),e=s(96540);function p(n){const a=Object.assign({h1:"h1",a:"a",span:"span",p:"p",blockquote:"blockquote",strong:"strong",h2:"h2",ul:"ul",li:"li",h3:"h3",ol:"ol",table:"table",thead:"thead",tr:"tr",th:"th",tbody:"tbody",td:"td",em:"em"},(0,t.R)(),n.components);return e.createElement(e.Fragment,null,e.createElement(a.h1,{id:"rsc-migration-component-tree-restructuring-patterns",style:{position:"relative"}},e.createElement(a.a,{href:"#rsc-migration-component-tree-restructuring-patterns","aria-label":"rsc migration component tree restructuring patterns permalink",className:"anchor before"},e.createElement(a.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 Migration: Component Tree Restructuring Patterns"),"\n",e.createElement(a.p,null,"This guide covers how to restructure your React component tree when migrating to React Server Components. The core challenge: your existing components likely mix data fetching, state management, and rendering in ways that prevent them from being Server Components. This guide shows you how to untangle them."),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Part 2 of the ",e.createElement(a.a,{href:"/linked-assets/e8d09fae987585e61153eab1eac11f7c/migrating-to-rsc.md"},"RSC Migration Series"))," | Previous: ",e.createElement(a.a,{href:"/linked-assets/d8189901c303abf9585e0bf12f108c91/rsc-preparing-app.md"},"Preparing Your App")," | Next: ",e.createElement(a.a,{href:"/linked-assets/cf64eb714346a803f0f5663af489ba73/rsc-context-and-state.md"},"Context and State Management")),"\n"),"\n",e.createElement(a.h2,{id:"the-mental-model-shift",style:{position:"relative"}},e.createElement(a.a,{href:"#the-mental-model-shift","aria-label":"the mental model shift permalink",className:"anchor before"},e.createElement(a.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>'}})),"The Mental Model Shift"),"\n",e.createElement(a.p,null,"In the RSC world, components are ",e.createElement(a.strong,null,"Server Components by default"),". You opt into client-side execution by adding ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," at the top of a file. The key rule:"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," operates at the module level, not the component level.")," Once a file is marked ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),", all its imports become part of the client bundle -- even if those imported components don't need client-side features."),"\n"),"\n",e.createElement(a.p,null,"This means the placement of ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directly determines your bundle size. The goal of restructuring is to push ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," as far down the component tree as possible, to leaf-level interactive elements."),"\n",e.createElement(a.p,null,"Keep this boundary separate from React on Rails file suffixes and RSC manifest discovery:"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.client.</code>'}})," and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.server.</code>'}})," suffixes decide which React on Rails bundle imports a file."),"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," decides whether React treats that module as a Client Component boundary."),"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})," decides which ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," modules the RSC plugin can discover and emit into\nthe client-reference manifests."),"\n"),"\n",e.createElement(a.p,null,"Those three controls need to stay aligned. A ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.server.jsx</code>'}})," file is not automatically a React Server\nComponent, and an empty ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})," list can hide a real Client Component from the manifest\neven when its ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," boundary is placed correctly. For the setup details, see\n",e.createElement(a.a,{href:"/linked-assets/d8189901c303abf9585e0bf12f108c91/rsc-preparing-app.md"},"Preparing Your App"),"\nand ",e.createElement(a.a,{href:"rsc-troubleshooting.md#client-reference-scope-and-empty-clientreferences"},"Client Reference Scope and Empty ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})),"."),"\n",e.createElement(a.h3,{id:"use-client-marks-a-boundary-not-a-component-type",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#use-client-marks-a-boundary-not-a-component-type","aria-label":"use client marks a boundary not a component type permalink",className:"anchor before"},e.createElement(a.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>'}})),e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," Marks a Boundary, Not a Component Type"),"\n",e.createElement(a.p,null,"A common misconception is that every component using hooks or browser APIs needs ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),". It doesn't. You only need the directive at the ",e.createElement(a.strong,null,"boundary")," — the file where code transitions from server to client. Everything imported below that boundary is automatically client code:"),"\n",e.createElement("p",{align:"center"},e.createElement("img",{src:"images/use-client-module-boundary.svg",alt:"Static diagram showing how the 'use client' directive works at the module (file) level. Once a file has the directive, all its imports automatically become client code — no additional directive needed in child modules.",width:"840"})),"\n",e.createElement(a.p,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">SearchInput</code>'}})," and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">SearchResults</code>'}})," use hooks and event handlers, but they don't need ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," because ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">SearchBar</code>'}})," already established the boundary. Adding it would be redundant."),"\n",e.createElement(a.h2,{id:"the-top-down-migration-strategy",style:{position:"relative"}},e.createElement(a.a,{href:"#the-top-down-migration-strategy","aria-label":"the top down migration strategy permalink",className:"anchor before"},e.createElement(a.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>'}})),"The Top-Down Migration Strategy"),"\n",e.createElement(a.p,null,"Start from the top of each component tree and work downward:"),"\n",e.createElement("p",{align:"center"},e.createElement("img",{src:"images/top-down-migration.svg",alt:"Animated three-phase diagram showing RSC migration strategy: Phase 1 marks the root as 'use client' (100% client bundle), Phase 2 pushes the boundary down to child components (~40% client), Phase 3 splits mixed components into server+client leaves (~15% client). Each phase reveals progressively with staggered animation.",width:"840"})),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React on Rails multi-root note:")," Unlike single-page apps with one root component, React on Rails renders multiple independent component trees on a page -- each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," call in your view is a separate root. This is actually an advantage for migration: you can migrate ",e.createElement(a.strong,null,"one registered component at a time"),", leaving the rest untouched."),"\n"),"\n",e.createElement(a.h3,{id:"phase-1-mark-all-entry-points-as-client-already-done",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#phase-1-mark-all-entry-points-as-client-already-done","aria-label":"phase 1 mark all entry points as client already done permalink",className:"anchor before"},e.createElement(a.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>'}})),"Phase 1: Mark All Entry Points as Client (already done)"),"\n",e.createElement(a.p,null,"If you followed ",e.createElement(a.a,{href:"/linked-assets/d8189901c303abf9585e0bf12f108c91/rsc-preparing-app.md"},"Preparing Your App"),", this phase is already complete. Every registered component entry point has ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),", so the RSC pipeline is active but all components are still Client Components. Ensure that your bundle root files (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">client-bundle.js</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">server-bundle.js</code>'}}),") do ",e.createElement(a.strong,null,"not")," have a ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directive -- only the individual component files should."),"\n",e.createElement(a.h3,{id:"phase-2-pick-a-component-and-push-the-boundary-down",style:{position:"relative"}},e.createElement(a.a,{href:"#phase-2-pick-a-component-and-push-the-boundary-down","aria-label":"phase 2 pick a component and push the boundary down permalink",className:"anchor before"},e.createElement(a.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>'}})),"Phase 2: Pick a Component and Push the Boundary Down"),"\n",e.createElement(a.p,null,"Choose one registered component to migrate. The ideal first candidate is a component that is mostly presentational -- heavy on layout and display, light on interactivity."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Step 1: Remove ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," from the component entry point.")," This makes it a Server Component."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Step 2: Update the registration.")," When a component loses its ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directive, its registration must change:"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"With ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">auto_load_bundle</code>'}}),":")," This happens automatically. The generated pack switches from ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails.register</code>'}})," to ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">registerServerComponent</code>'}})," based on whether the file has ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}}),". For the full details on how auto-bundling classifies and registers RSC components, see ",e.createElement(a.a,{href:"../core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md#auto-bundling-with-react-server-components"},"Auto-Bundling with React Server Components"),"."),"\n"),"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"With manual registration:")," The ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">registerServerComponent</code>'}})," API uses different import paths per bundle (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">/server</code>'}})," vs ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">/client</code>'}}),"). How you update registration depends on your current setup:"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"If you have a single bundle file")," (e.g., ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">server-bundle.js</code>'}})," that imports and registers all components):"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// server-bundle.js</span>\n<span class="token keyword">
1import</span> registerServerComponent <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro/registerServerComponent/server\'</span><span class="token punctuation">;</span>\n\n<span class="token comment">// Migrated component -- now a Server Component</span>\n<span class="token keyword">import</span> ProductPage <span class="token keyword">from</span> <span class="token string">\'../components/ProductPage\'</span><span class="token punctuation">;</span>\n<span class="token function">registerServerComponent</span><span class="token punctuation">(</span><span class="token punctuation">{</span> ProductPage <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n<span class="token comment">// Not yet migrated -- still Client Components</span>\n<span class="token keyword">import</span> ReactOnRails <span class="token keyword">from</span> <span class="token string">\'react-on-rails\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span>
1 CartPage <span class="token keyword">from</span> <span class="token string">\'../components/CartPage\'</span><span class="token punctuation">;</span>\nReactOnRails<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token punctuation">{</span> CartPage <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",e.createElement(a.p,null,"On the client side, create a separate entry point for each migrated component:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// ProductPage.client.js -- client entry point for the migrated component</span>\n<span class="token keyword">import</span> registerServerComponent <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro/registerServerComponent/client\'</span><span class="token punctuation">;</span>\n\n<span class="token function">registerServerComponent</span><span class="token punctuation">(</span><span class="token string">\'ProductPage\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",e.createElement(a.p,null,"Add ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.client.js</code>'}})," as a client bundle entry point in your webpack config."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"If each component has its own entry file")," (e.g., ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.jsx</code>'}})," contains ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails.register</code>'}})," and is used as a client bundle entry point):"),"\n",e.createElement(a.ol,null,"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Remove")," the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails.register</code>'}})," call from ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.jsx</code>'}}),"."),"\n"),"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Create")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.client.jsx</code>'}})," with the client-side registration:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// ProductPage.client.jsx -- replaces ProductPage.jsx as the client entry point</span>\n<span class="token keyword">import</span> registerServerComponent <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro/registerServerComponent/client\'</span><span class="token punctuation">;</span>\n\n<span class="token function">registerServerComponent</span><span class="token punctuation">(</span><span class="token string">\'ProductPage\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n"),"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"In the server bundle"),", import the component and register it:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// server-bundle.js (or a dedicated server entry file)</span>\n<span class="token keyword">import</span> registerServerComponent <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro/registerServerComponent/server\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span>
1 ProductPage <span class="token keyword">from</span> <span class="token string">\'../components/ProductPage\'</span><span class="token punctuation">;</span>\n\n<span class="token function">registerServerComponent</span><span class="token punctuation">(</span><span class="token punctuation">{</span> ProductPage <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n"),"\n",e.createElement(a.li,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Update your client webpack config")," to use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.client.jsx</code>'}})," as the entry point instead of ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage.jsx</code>'}}),". This preserves per-component chunking on the client side."),"\n"),"\n"),"\n"),"\n"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Step 3: Push ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," down to interactive children.")," Identify child components that don't use hooks or browser APIs. Those can stay as server-rendered. Add ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," only to the children that need interactivity."),"\n",e.createElement("p",{align:"center"},e.createElement("img",{src:"images/push-boundary-down.svg",alt:"Static before-and-after diagram showing how to migrate a React component tree by pushing the 'use client' directive from the root down to only the leaf components that truly need interactivity. Before: the entire tree is client code. After: only interactive leaves are client components.",width:"840"})),"\n",e.createElement(a.p,null,"Repeat for each registered component you want to migrate."),"\n",e.createElement(a.h3,{id:"phase-3-split-mixed-components",style:{position:"relative"}},e.createElement(a.a,{href:"#phase-3-split-mixed-components","aria-label":"phase 3 split mixed components permalink",className:"anchor before"},e.createElement(a.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>'}})),"Phase 3: Split Mixed Components"),"\n",e.createElement(a.p,null,"Components that mix data fetching with interactivity need to be split into two parts."),"\n",e.createElement(a.h2,{id:"pattern-1-pushing-state-to-leaf-components",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-1-pushing-state-to-leaf-components","aria-label":"pattern 1 pushing state to leaf components permalink",className:"anchor before"},e.createElement(a.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>'}})),"Pattern 1: Pushing State to Leaf Components"),"\n",e.createElement(a.p,null,"The most common restructuring pattern. When a parent component has state that only a small part of the tree needs, extract the stateful part into a dedicated Client Component."),"\n",e.createElement(a.h3,{id:"before-state-at-the-top-blocks-everything",style:{position:"relative"}},e.createElement(a.a,{href:"#before-state-at-the-top-blocks-everything","aria-label":"before state at the top blocks everything permalink",className:"anchor before"},e.createElement(a.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>'}})),"Before: State at the top blocks everything"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token string">\'use client\'</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">ProductPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> productId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>product<span class="token punctuation">,</span> setProduct<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>quantity<span class="token punctuation">,</span> setQuantity<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n  <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n    <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/api/products/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>productId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
1\n      <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> res<span class="token punctuation">.</span><span class="token function">json</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 function">then</span><span class="token punctuation">(</span>setProduct<span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>productId<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>product<span class="token punctuation">)</span> <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">Loading</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span>\n\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>div</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>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><span class="token class-name">ProductSpecs</span></span> <span class="token attr-name">specs</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>specs<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><span class="token class-name">ReviewList</span></span> <span class="token attr-name">reviews</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>reviews<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>div</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>input</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>number<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>quantity<span class="token punctuation">}</span></span> <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setQuantity</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span><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>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">
1addToCart</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>id<span class="token punctuation">,</span> quantity<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">Add to Cart</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</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>div</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>div</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",e.createElement(a.h3,{id:"after-state-pushed-to-a-leaf-data-from-rails-props",style:{position:"relative"}},e.createElement(a.a,{href:"#after-state-pushed-to-a-leaf-data-from-rails-props","aria-label":"after state pushed to a leaf data from rails props permalink",className:"anchor before"},e.createElement(a.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>'}})),"After: State pushed to a leaf, data from Rails props"),"\n",e.createElement(a.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"><span class="token comment"># ERB view — Rails passes the data as props </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"> stream_react_component<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"ProductPage"</span></span><span class="token punctuation">,</span>\n      <span class="token symbol">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">product</span><span class="token operator">:</span> <span class="token variable">@product</span><span class="token punctuation">.</span>as_json<span class="token punctuation">(</span>\n                 <span class="token symbol">include</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">specs</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">only</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token symbol">:id</span><span class="token punctuation">,</span> <span class="token symbol">:label</span><span class="token punctuation">,</span> <span class="token symbol">:value</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>\n                            <span class="token symbol">reviews</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">only</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token symbol">:id</span><span class="token punctuation">,</span> <span class="token symbol">:text</span><span class="token punctuation">,</span> <span class="token symbol">:rating</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// ProductPage.jsx -- Server Component (no directive)</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">ProductPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> product <span class="token punctuation">}</span></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>div</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>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><span class="token class-name">ProductSpecs</span></span> <span class="token attr-name">specs</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>specs<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><span class="token class-name">ReviewList</span></span> <span class="token attr-name">reviews</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>reviews<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><span class="token class-name">AddToCartButton</span></span> <span class="token attr-name">productId</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>id<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>div</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",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// AddToCartButton.jsx -- Client Component (interactive leaf)</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> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> addToCart <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'../api\'</span><span class="token punctuation">;</span> <span class="token comment">// Calls a Rails controller endpoint</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">AddToCartButton</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> productId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>quantity<span class="token punctuation">,</span> setQuantity<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\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>div</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>input</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>number<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>quantity<span class="token punctuation">}</span></span> <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setQuantity</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span><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>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">addToCart</span><span class="token punctuation">(</span>productId<span class="token punctuation">,</span> quantity<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">Add to Cart</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</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>div</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",e.createElement(a.p,null,e.createElement(a.strong,null,"Result:")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductSpecs</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReviewList</code>'}}),", and all the product data rendering stay on the server. Only the add-to-cart interaction ships JavaScript to the client."),"\n",e.createElement(a.h2,{id:"pattern-2-the-donut-pattern-children-as-props",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#pattern-2-the-donut-pattern-children-as-props","aria-label":"pattern 2 the donut pattern children as props permalink",className:"anchor before"},e.createElement(a.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>'}})),"Pattern 2: The Donut Pattern (Children as Props)"),"\n",e.createElement(a.p,null,"When a Client Component needs to wrap Server Component content, use the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">children</code>'}}),' prop to create a "hole" in the client boundary. The Server Component content passes through without becoming client code.'),"\n",e.createElement(a.h3,{id:"the-problem",style:{position:"relative"}},e.createElement(a.a,{href:"#the-problem","aria-label":"the problem permalink",className:"anchor before"},e.createElement(a.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>'}})),"The Problem"),"\n",e.createElement(a.p,null,"A modal needs client-side state to toggle visibility, but its content should be server-rendered:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// This would make Cart a Client Component (wrong!)</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> Cart <span class="token keyword">from</span> <span class="token string">\'./Cart\'</span><span class="token punctuation">;</span> <span class="token comment">// Cart becomes client code via import chain</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">Modal</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>isOpen<span class="token punctuation">,</span> setIsOpen<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</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>div</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>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setIsOpen</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">Open</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>isOpen <span class="token operator">&amp;&amp;</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">
1Cart</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">}</span><span class="token plain-text">\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</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",e.createElement(a.h3,{id:"the-solution",style:{position:"relative"}},e.createElement(a.a,{href:"#the-solution","aria-label":"the solution permalink",className:"anchor before"},e.createElement(a.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>'}})),"The Solution"),"\n",e.createElement(a.p,null,"Use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">children</code>'}})," to pass the Server Component through without importing it:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// Modal.jsx -- Client Component</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react\'</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">Modal</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>isOpen<span class="token punctuation">,</span> setIsOpen<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</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>div</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>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setIsOpen</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">Open Cart</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>isOpen <span class="token operator">&amp;&amp;</span> children<span class="token punctuation">}</span><span class="token plain-text">\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</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",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// Page.jsx -- Server Component (composes both)</span>\n<span class="token keyword">
1import</span> Modal <span class="token keyword">from</span> <span class="token string">\'./Modal\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> Cart <span class="token keyword">from</span> <span class="token string">\'./Cart\'</span><span class="token punctuation">;</span> <span class="token comment">// Cart stays 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">Page</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 class="token class-name">Modal</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><span class="token class-name">Cart</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span><span class="token comment">/* Rendered on the server, passed through */</span><span class="token punctuation">}</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">Modal</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",e.createElement(a.p,null,e.createElement(a.strong,null,"Why this works:")," The Server Component (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Page</code>'}}),') is the "owner" -- it decides what ',e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Cart</code>'}})," receives as props and renders it on the server. ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Modal</code>'}})," receives pre-rendered content as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">children</code>'}}),", not the component definition."),"\n",e.createElement(a.h2,{id:"pattern-3-extracting-state-into-a-wrapper-component",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-3-extracting-state-into-a-wrapper-component","aria-label":"pattern 3 extracting state into a wrapper component permalink",className:"anchor before"},e.createElement(a.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>'}})),"Pattern 3: Extracting State into a Wrapper Component"),"\n",e.createElement(a.p,null,"When a feature (like theme switching) requires state high in the tree, extract the stateful logic into a dedicated wrapper component so the rest of the tree stays server-rendered."),"\n",e.createElement(a.h3,{id:"before-theme-state-forces-everything-client-side",style:{position:"relative"}},e.createElement(a.a,{href:"#before-theme-state-forces-everything-client-side","aria-label":"before theme state forces everything client side permalink",className:"anchor before"},e.createElement(a.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>'}})),"Before: Theme state forces everything client-side"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token string">\'use client\'</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">Homepage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>theme<span class="token punctuation">,</span> setTheme<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">\'light\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span> colors <span class="token operator">=</span> theme <span class="token operator">===</span> <span class="token string">\'light\'</span> <span class="token operator">?</span> <span class="token constant">LIGHT_COLORS</span> <span class="token operator">:</span> <span class="token constant">DARK_COLORS</span><span class="token punctuation">;</span>\n\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>body</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>colors<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><span class="token class-name">Header</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><span class="token class-name">MainContent</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><span class="token class-name">Footer</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>body</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",e.createElement(a.h3,{id:"after-theme-wrapper-isolates-state",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#after-theme-wrapper-isolates-state","aria-label":"after theme wrapper isolates state permalink",className:"anchor before"},e.createElement(a.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>'}})),"After: Theme wrapper isolates state"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// ColorProvider.jsx -- Client Component (state only)</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react\'</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">ColorProvider</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>theme<span class="token punctuation">,</span> setTheme<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">\'light\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span> colors <span class="token operator">=</span> theme <span class="token operator">===</span> <span class="token string">\'light\'</span> <span class="token operator">?</span> <span class="token constant">LIGHT_COLORS</span> <span class="token operator">:</span> <span class="token constant">DARK_COLORS</span><span class="token punctuation">;</span>\n\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</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>colors<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// Homepage.jsx -- Server Component (owns the tree)</span>\n<span class="token keyword">import</span> ColorProvider <span class="token keyword">from</span> <span class="token string">\'./ColorProvider\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> Header <span class="token keyword">from</span> <span class="token string">\'./Header\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> MainContent <span class="token keyword">from</span> <span class="token string">\'./MainContent\'</span><span class="token punctuation">;</span>\n<span class="token keyword">
1import</span> Footer <span class="token keyword">from</span> <span class="token string">\'./Footer\'</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">Homepage</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 class="token class-name">ColorProvider</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><span class="token class-name">Header</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span><span class="token comment">/* Stays a Server Component */</span><span class="token punctuation">}</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">MainContent</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span><span class="token comment">/* Stays a Server Component */</span><span class="token punctuation">}</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">Footer</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span><span class="token comment">/* Stays a Server Component */</span><span class="token punctuation">}</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">ColorProvider</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",e.createElement(a.p,null,e.createElement(a.strong,null,"Key insight:")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Homepage</code>'}})," (a Server Component) is the component that imports and renders ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Header</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">MainContent</code>'}}),", and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Footer</code>'}}),". Since ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Homepage</code>'}})," owns these children, they remain Server Components -- even though they're visually nested inside the Client Component ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ColorProvider</code>'}}),"."),"\n",e.createElement(a.h2,{id:"pattern-4-streaming-with-stream_react_component",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-4-streaming-with-stream_react_component","aria-label":"pattern 4 streaming with stream_react_component permalink",className:"anchor before"},e.createElement(a.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>'}})),"Pattern 4: Streaming with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})),"\n",e.createElement(a.p,null,"In React on Rails, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," uses React's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">renderToPipeableStream</code>'}})," to stream rendered HTML to the browser as React processes the component tree. Rails loads all data synchronously and passes it as props:"),"\n",e.createElement(a.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"><span class="token comment"># ERB view — Rails passes all data as props </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"> stream_react_component<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Dashboard"</span></span><span class="token punctuation">,</span>
1\n      <span class="token symbol">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">title</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"Dashboard"</span></span><span class="token punctuation">,</span>\n               <span class="token symbol">stats</span><span class="token operator">:</span> DashboardStats<span class="token punctuation">.</span>compute<span class="token punctuation">.</span>as_json<span class="token punctuation">,</span>\n               <span class="token symbol">revenue</span><span class="token operator">:</span> RevenueChart<span class="token punctuation">.</span>data<span class="token punctuation">.</span>as_json<span class="token punctuation">,</span>\n               <span class="token symbol">orders</span><span class="token operator">:</span> Order<span class="token punctuation">.</span>recent<span class="token punctuation">.</span>as_json <span class="token punctuation">}</span><span class="token punctuation">)</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// Dashboard.jsx -- Server Component</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Dashboard</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> title<span class="token punctuation">,</span> stats<span class="token punctuation">,</span> revenue<span class="token punctuation">,</span> orders <span class="token punctuation">}</span></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>div</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>title<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">Stats</span></span> <span class="token attr-name">stats</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>stats<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><span class="token class-name">RevenueChart</span></span> <span class="token attr-name">revenue</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>revenue<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><span class="token class-name">RecentOrders</span></span> <span class="token attr-name">orders</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>orders<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>div</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span>\n\n<span class="token comment">// Stats.jsx -- Server Component (renders directly from props)</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Stats</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> stats <span class="token punctuation">}</span></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>div</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</span><span class="token punctuation">></span></span><span class="token plain-text">Revenue: </span><span class="token punctuation">{</span>stats<span class="token punctuation">.</span>revenue<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>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>span</span><span class="token punctuation">></span></span><span class="token plain-text">Users: </span><span class="token punctuation">{</span>stats<span class="token punctuation">.</span>users<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>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>div</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",e.createElement(a.p,null,"Rails loads all data as props before rendering begins. ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," then streams the rendered HTML to the browser as React processes the component tree — no client-side fetching or loading states needed. For slow Rails data that should not block the initial shell, use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),"; see ",e.createElement(a.a,{href:"rsc-data-fetching.md#data-fetching-in-react-on-rails-pro"},"Data Fetching in React on Rails Pro"),"."),"\n",e.createElement(a.h2,{id:"pattern-
15-server-data-to-interactive-client-components",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-5-server-data-to-interactive-client-components","aria-label":"pattern 5 server data to interactive client components permalink",className:"anchor before"},e.createElement(a.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>'}})),"Pattern 5: Server Data to Interactive Client Components"),"\n",e.createElement(a.p,null,"Pass server-fetched data from Rails to a Client Component that adds interactivity. The Server Component receives the data as props and passes it to the Client Component:"),"\n",e.createElement(a.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"><span class="token comment"># ERB view — Rails passes all data as props </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"> stream_react_component<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"PostPage"</span></span><span class="token punctuation">,</span>\n      <span class="token symbol">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">title</span><span class="token operator">:</span> post<span class="token punctuation">.</span>title<span class="token punctuation">,</span>\n               <span class="token symbol">body</span><span class="token operator">:</span> post<span class="token punctuation">.</span>body<span class="token punctuation">,</span>\n               <span class="token symbol">comments</span><span class="token operator">:</span> post<span class="token punctuation">.</span>comments<span class="token punctuation">.</span>includes<span class="token punctuation">(</span><span class="token symbol">:author</span><span class="token punctuation">)</span><span class="token punctuation">.</span>as_json <span class="token punctuation">}</span><span class="token punctuation">)</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// PostPage.jsx -- Server Component</span>\n<span class="token keyword">import</span> Comments <span class="token keyword">from</span> <span class="token string">\'./Comments\'</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">PostPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> title<span class="token punctuation">,</span> body<span class="token punctuation">,</span> comments <span class="token punctuation">}</span></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 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>title<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>p</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>body<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><span class="token class-name">Comments</span></span> <span class="token attr-name">comments</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>comments<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>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",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// Comments.jsx -- Client Component (adds interactivity like reply buttons)</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> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react\'</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">Comments</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> comments <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>expanded<span class="token punctuation">,</span> setExpanded<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>comments<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>\n        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>c<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">\n          </span><span class="token punctuation">{</span>c<span class="token punctuation">.</span>text<span class="token punctuation">}</span><span class="token plain-text">\n          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setExpanded</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">prev</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>prev<span class="token punctuation">,</span> <span class="token punctuation">[</span>c<span class="token punctuation">.</span>id<span class="token punctuation">]</span><span class="token operator">:</span> <span class="token operator">!</span>prev<span class="token punctuation">[</span>c<span class="token punctuation">.</span>id<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">\n            </span><span class="token punctuation">{</span>expanded<span class="token punctuation">[</span>c<span class="token punctuation">.</span>id<span class="token punctuation">]</span> <span class="token operator">?</span> <span class="token string">\'Collapse\'</span> <span class="token operator">:</span> <span class="token string">
1\'Reply\'</span><span class="token punctuation">}</span><span class="token plain-text">\n          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</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>li</span><span class="token punctuation">></span></span>\n      <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">\n    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</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",e.createElement(a.p,null,e.createElement(a.strong,null,"Benefits:")," The Server Component handles data display with zero JavaScript cost. The Client Component receives pre-fetched data as props and adds only the interactivity it needs (reply buttons, expand/collapse)."),"\n",e.createElement(a.h2,{id:"decision-guide-server-or-client-component",style:{position:"relative"}},e.createElement(a.a,{href:"#decision-guide-server-or-client-component","aria-label":"decision guide server or client component permalink",className:"anchor before"},e.createElement(a.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>'}})),"Decision Guide: Server or Client Component?"),"\n",e.createElement(a.table,null,e.createElement(a.thead,null,e.createElement(a.tr,null,e.createElement(a.th,null,"Feature Needed"),e.createElement(a.th,null,"Component Type"),e.createElement(a.th,null,"Reason"))),e.createElement(a.tbody,null,e.createElement(a.tr,null,e.createElement(a.td,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useState</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useReducer</code>'}})),e.createElement(a.td,null,"Client"),e.createElement(a.td,null,"State requires re-rendering")),e.createElement(a.tr,null,e.createElement(a.td,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useLayoutEffect</code>'}})),e.createElement(a.td,null,"Client"),e.createElement(a.td,null,"Lifecycle effects run in browser")),e.createElement(a.tr,null,e.createElement(a.td,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">onClick</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">onChange</code>'}}),", event handlers"),e.createElement(a.td,null,"Client"),e.createElement(a.td,null,"Events are browser-only")),e.createElement(a.tr,null,e.createElement(a.td,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">window</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">document</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">localStorage</code>'}})),e.createElement(a.td,null,"Client"),e.createElement(a.td,null,"Browser APIs")),e.createElement(a.tr,null,e.createElement(a.td,null,"Custom hooks using the above"),e.createElement(a.td,null,"Client"),e.createElement(a.td,null,"Transitively client")),e.createElement(a.tr,null,e.createElement(a.td,null,"Data fetching (database, API)"),e.createElement(a.td,null,"Server"),e.createElement(a.td,null,"Direct backend access, no bundle cost")),e.createElement(a.tr,null,e.createElement(a.td,null,"Rendering static/display-only content"),e.createElement(a.td,null,"Server*"),e.createElement(a.td,null,"No JavaScript shipped")),e.createElement(a.tr,null,e.createElement(a.td,null,"Using server-only secrets (API keys)"),e.createElement(a.td,null,"Server"),e.createElement(a.td,null,"Never exposed to client")),e.createElement(a.tr,null,e.createElement(a.td,null,"Heavy depen
1dencies (Markdown parsers, formatters)"),e.createElement(a.td,null,"Server"),e.createElement(a.td,null,"Dependencies stay off client bundle")))),"\n",e.createElement(a.p,null,e.createElement(a.em,null,"*For components repeated many times with verbose markup (e.g., Tailwind utility classes), Server Component rendering can inflate the Flight payload. In those cases, a Client Component may produce a smaller page. See ",e.createElement(a.a,{href:"/linked-assets/2d3c2bba9ce84a8894c63415e7e271a4/rsc-flight-payload.md"},"Flight Payload Optimization")," for details.")),"\n",e.createElement(a.h2,{id:"common-mistakes",style:{position:"relative"}},e.createElement(a.a,{href:"#common-mistakes","aria-label":"common mistakes permalink",className:"anchor before"},e.createElement(a.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 Mistakes"),"\n",e.createElement(a.h3,{id:"mistake-1-adding-use-client-too-high",style:{position:"relative"}},e.createElement(a.a,{href:"#mistake-1-adding-use-client-too-high","aria-label":"mistake 1 adding use client too high permalink",className:"anchor before"},e.createElement(a.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>'}})),"Mistake 1: Adding ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," too high"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// BAD: Makes the entire layout client-side</span>\n<span class="token string">\'use client\'</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">Layout</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children <span class="token punctuation">}</span></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>div</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>nav</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">Logo</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><span class="token class-name">SearchBar</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>nav</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>main</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</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>footer</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>footer</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>div</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",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// GOOD: Only SearchBar is client-side</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Layout</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children <span class="token punctuation">}</span></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>div</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>nav</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Logo</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">SearchBar</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</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>main</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</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>footer</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>footer</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>div</span><span class="token punctuation">></span></span>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span>\n\n<span class="token comment">// SearchBar.jsx</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">SearchBar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* interactive logic */</span> <span class="token punctuation">}</span></code></pre></div>'}}),"\n",e.createElement(a.h3,{id:"mistake-2-importing-server-components-in-client-components",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mistake-2-importing-server-components-in-client-components","aria-label":"mistake 2 importing server components in client components permalink",className:"anchor before"},e.createElement(a.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>'}})),"Mistake 2: Importing Server Components in Client Components"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// BAD: ServerComponent becomes a client component via import</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> ServerComponent <span class="token keyword">from</span> <span class="token string">\'./ServerComponent\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">ClientWrapper</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">ServerComponent</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span> <span class="token comment">// This is now client code!</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// GOOD: Pass Server Components as children</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">ClientWrapper</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children <span class="token punctuation">}</span></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 punctuation">></span></span><span class="token punctuation">{</span>children<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>\n\n<span class="token comment">// In a Server Component parent:</span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">ClientWrapper</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><span class="token class-name">ServerComponent</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span><span class="token comment">/* Stays a Server Component */</span><span class="token punctuation">}</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">ClientWrapper</span></span><span class="token punctuation">></span></span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",e.createElement(a.h3,{id:"mistake-3-chunk-contamination-from-shared-use-client-files",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mistake-3-chunk-contamination-from-shared-use-client-files","aria-label":"mistake 3 chunk contamination from shared use client files permalink",className:"anchor before"},e.createElement(a.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>'}})),"Mistake 3: Chunk contamination from shared ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," files"),"\n",e.createElement(a.p,null,"If your RSC page downloads unexpectedly large chunks, a shared ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," component may accumulate chunks from multiple entry paths (including heavy SSR/client paths with unrelated dependencies). This can cause the browser to download hundreds of kilobytes of JavaScript it doesn't need. See ",e.createElement(a.a,{href:"rsc-troubleshooting.md#chunk-contamination"},"Chunk Contamination")," for wrapper and prop-injection fixes."),"\n",e.createElement(a.h3,{id:"mistake-4-emptying-clientreferences-for-a-mixed-rsc-app",style:{position:"relative"}},e.createElement(a.a,{href:"#mistake-4-emptying-clientreferences-for-a-mixed-rsc-app","aria-label":"mistake 4 emptying clientreferences for a mixed rsc app permalink",className:"anchor before"},e.createElement(a.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>'}})),"Mistake 4: Emptying ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})," for a mixed RSC app"),"\n",e.createElement(a.p,null,"A static RSC route with no client islands may still render when ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})," is empty, but a\nmixed app can fail later when another route adds a real Client Component. Treat an empty list as a\nstatic-only build constraint, not a general restructuring pattern."),"\n",e.createElement(a.p,null,"Browser sidecars do not change this rule. A sidecar is plain browser JavaScript outside the RSC\npayload; its success does not prove the RSC client-reference manifest can hydrate future client\nislands. Keep sidecar behavior separate from RSC client boundaries, and smoke-test at least one RSC\nroute with a real Client Component whenever ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})," is narrowed."),"\n",e.createElement(a.p,null,"See ",e.createElement(a.a,{href:"rsc-troubleshooting.md#client-reference-scope-and-empty-clientreferences"},"Client Reference Scope and Empty ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">clientReferences</code>'}})),"\nfor the decision table and detection steps."),"\n",e.createElement(a.h3,{id:"mistake-5-confusing-use-client-with-use-server",style:{position:"relative"}},e.createElement(a.a,{href:"#mistake-5-confusing-use-client-with-use-server","aria-label":"mistake 5 confusing use client with use server permalink",className:"anchor before"},e.createElement(a.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>'}})),"Mistake 5: Confusing ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}})),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," marks a file's components as ",e.createElement(a.strong,null,"Client Components")),"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}})," marks ",e.createElement(a.strong,null,"Server Actions")," (functions callable from the client) -- NOT Server Components"),"\n",e.createElement(a.li,null,"Server Components are the ",e.createElement(a.strong,null,"default")," and need no directive"),"\n"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React on Rails note:")," Server Actions (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}}),") are ",e.createElement(a.strong,null,"not supported")," in React on Rails. Server Actions run on the Node renderer, which has no access to Rails models, sessions, cookies, or CSRF protection. Use Rails controllers for all mutations. See ",e.createElement(a.a,{href:"rsc-data-fetching.md#mutations-rails-controllers-not-server-actions"},"Mutations: Rails Controllers, Not Server Actions"),"."),"\n"),"\n",e.createElement(a.h2,{id:"next-steps",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#next-steps","aria-label":"next steps permalink",className:"anchor before"},e.createElement(a.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>'}})),"Next Steps"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,e.createElement(a.a,{href:"/linked-assets/cf64eb714346a803f0f5663af489ba73/rsc-context-and-state.md"},"Context, Providers, and State Management")," -- how to handle Context and global state"),"\n",e.createElement(a.li,null,e.createElement(a.a,{href:"/linked-assets/ac508aaab32d6164befba4ceae1e5634/rsc-data-fetching.md"},"Data Fetching Migration")," -- migrating from useEffect to server-side fetching"),"\n",e.createElement(a.li,null,e.createElement(a.a,{href:"/linked-assets/acfb09bf7dbe0fddd7b750d34481c9c4/rsc-third-party-libs.md"},"Third-Party Library Compatibility")," -- dealing with incompatible libraries"),"\n",e.createElement(a.li,null,e.createElement(a.a,{href:"/linked-assets/95b3d10509bb106251ff4d4866187802/rsc-troubleshooting.md"},"Troubleshooting and Common Pitfalls")," -- debugging and avoiding problems"),"\n"))}var o=function(n){void 0===n&&(n={});const{wrapper:a}=Object.assign({},(0,t.R)(),n.components);return a?e.createElement(a,n,e.createElement(p,n)):p(n)},c=s(63679),l=s(95197),r=s(47388),i=s(23690),u=s(14487);const k=c.default.p.withConfig({displayName:"doc-article__RedirectFallback",componentId:"sc-tic2w5-0"})(["padding:2rem;"]),d=n=>{let{data:a,location:s,children:t}=n;const{headings:p}=a.article.childMdx,o=(0,l.X9)(s.pathname),[c,i]=e.useState(null),d=s.pathname.split("/"),m=d.indexOf("docs"),g=-1===m?s.pathname.length:d.slice(0,m+1).join("/").length,h=s.pathname.slice(0,g),y=s.pathname.slice(g+1).replace(/\/$/,"").split("/").filter((n=>""!==n)),v=y.length>1?y.slice(0,-1).join("/"):"";e.useEffect((()=>{if(!o)return;const{hash:n}=window.location;i(n);const a=""+o+n;window.location.href!==a&&window.location.replace(a)}),[o]);const f=null===o||null===c?null:""+o+c;return e.createElement(e.Fragment,null,null!==f?e.createElement(k,{role:"status","aria-live":"polite"},"Redirecting..."," ",e.createElement("a",{href:f,target:"_blank",rel:"noopener noreferrer"},"Click here if not redirected.")):e.createElement(r.A,{main:e.createElement(u.A,{body:t,headings:p,transformUrl:n=>{if(n.startsWith("http")||n.startsWith("#"))return n;const a=n.replace(/\.md$/,"/");if(a.startsWith("./"))return h+(""===v?"":"/"+v)+a.slice(1);if(a.startsWith("../")){if(""===v)return""+h+a.slice(2);const n=v.split("/").slice(0,-1).join("/");return h+(""===n?"":"/"+n)+a.slice(2)}return h+"/"+(""===v?"":v+"/")+a}})}))};function m(n){return e.createElement(d,n,e.createElement(o,n))}const g=n=>{let{data:a,location:s,pageContext:t}=n;const{headings:p}=a.article.childMdx,{siteUrl:o}=a.site.siteMetadata,{repoDisplayName:c}=t,r=p.find((n=>1===n.depth)),u=(0,l.X9)(s.pathname)||""+o.replace(/\/+$/,"")+s.pathname;return e.createElement(i.A,{title:r?r.value:c,description:r?r.value:c+" documentation",isArticle:!0,canonical:u,pathname:s.pathname})}}}]);
2//# sourceMappingURL=component---src-template-doc-article-tsx-content-file-path-cache-gatsby-source-git-react-on-rails-docs-oss-migrating-rsc-component-patterns-md-9b5c101590dbccd1ccea.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.