PageSourceSearch

https://shakacode.com/d84bad82-b83c3f5fac295951a043.js

js shakacode.com collected 2026-10-04 00:24:43 UTC 262,407 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkgatsby_starter_default=self.webpackChunkgatsby_starter_default||[]).push([[284],{78173:function(n,a,s){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",h3:"h3",ul:"ul",li:"li",div:"div",svg:"svg",path:"path",ol:"ol",em:"em",img:"img",table:"table",thead:"thead",tr:"tr",th:"th",tbody:"tbody",td:"td",h4:"h4"},(0,t.R)(),n.components);return e.createElement(e.Fragment,null,e.createElement(a.h1,{id:"rsc-migration-data-fetching-patterns",style:{position:"relative"}},e.createElement(a.a,{href:"#rsc-migration-data-fetching-patterns","aria-label":"rsc migration data fetching 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: Data Fetching Patterns"),"\n",e.createElement(a.p,null,"This guide covers how to migrate your data fetching from client-side patterns (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}})," + ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">fetch</code>'}}),", React Query, SWR) to Server Component patterns. In React on Rails, data flows from Rails to your components as props — eliminating the need for loading states, error handling boilerplate, and client-side caching in many cases."),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Part 4 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/cf64eb714346a803f0f5663af489ba73/rsc-context-and-state.md"},"Context and State Management")," | Next: ",e.createElement(a.a,{href:"/linked-assets/cac8473589e96834cd83ae7a914ec0e2/rsc-http-response-patterns.md"},"HTTP Response Ownership")),"\n"),"\n",e.createElement(a.h2,{id:"the-core-shift-from-client-side-fetching-to-server-side-data",style:{position:"relative"}},e.createElement(a.a,{href:"#the-core-shift-from-client-side-fetching-to-server-side-data","aria-label":"the core shift from client side fetching to server side data 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 Core Shift: From Client-Side Fetching to Server-Side Data"),"\n",e.createElement(a.p,null,"In the traditional React model, components fetch data on the client after mounting. In the RSC model, data arrives from the server as props — the component simply renders it."),"\n",e.createElement(a.h3,{id:"before-client-side-fetching",style:{position:"relative"}},e.createElement(a.a,{href:"#before-client-side-fetching","aria-label":"before client side fetching 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: Client-Side Fetching"),"\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">
1import</span> <span class="token punctuation">{</span> useState<span class="token punctuation">,</span> useEffect <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">UserProfile</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> userId <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>user<span class="token punctuation">,</span> setUser<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>loading<span class="token punctuation">,</span> setLoading<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">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span> <span class="token punctuation">[</span>error<span class="token punctuation">,</span> setError<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\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/users/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>userId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>\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><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n        <span class="token function">setUser</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>\n        <span class="token function">setLoading</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 punctuation">}</span><span class="token punctuation">)</span>\n      <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
1\n        <span class="token function">setError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span>\n        <span class="token function">setLoading</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 punctuation">}</span><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>userId<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>loading<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">Spinner</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span>\n  <span class="token keyword">if</span> <span class="token punctuation">(</span>error<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">ErrorMessage</span></span> <span class="token attr-name">error</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>error<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></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>div</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>user<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>div</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.h3,{id:"after-server-component-with-async-props",style:{position:"relative"}},e.createElement(a.a,{href:"#after-server-component-with-async-props","aria-label":"after server component with async 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: Server Component with Async Props"),"\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">// UserPage.jsx -- Server Component (no directive needed)</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> Suspense <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">UserPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> getReactOnRailsAsyncProp <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><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Spinner</span></span> <span class="token punctuation">/></span></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><span class="token class-name">UserProfile</span></span> <span class="token attr-name">
1userPromise</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token function">getReactOnRailsAsyncProp</span><span class="token punctuation">(</span><span class="token string">\'user\'</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><span class="token class-name">Suspense</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>\n\n<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">UserProfile</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> userPromise <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> userPromise<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>user<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>div</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.p,null,"Rails streams the data through async props using ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),". The component uses ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}})," to obtain data as a Promise, and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," handles loading states — no client-side fetching, no ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useState</code>'}}),", no ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}}),"."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"What changed:")),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,"No ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useState</code>'}})," for data, loading, or error"),"\n",e.createElement(a.li,null,"No ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}})," lifecycle management"),"\n",e.createElement(a.li,null,"No ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directive"),"\n",e.createElement(a.li,null,"Data comes from Rails via async props — no client-side fetching"),"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," replaces manual loading/error checks"),"\n",e.createElement(a.li,null,"No JavaScript ships to the client for this component"),"\n"),"\n",e.createElement("p",{align:"center"},e.createElement("img",{src:"images/waterfall-vs-parallel.svg",alt:"Side-by-side comparison showing client-side fetching requiring an extra API round trip after mount versus Server Component pattern where Rails streams data via async props — no extra round trips, no client state management, Suspense handles loading.",width:"840"})),"\n",e.createElement(a.p,null,"For pages with multiple data sources, use ",e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}))," to\nstream the rendered HTML to the browser as React renders the component tree. When slower data sources should resolve\nindependently behind Suspense boundaries, use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),"."),"\n",e.createElement(a.h2,{id:"data-fetching-in-react-on-rails-pro",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro","aria-label":"data fetching in react on rails pro 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>'}})),"Data Fetching in React on Rails Pro"),"\n",e.createElement(a.p,null,"In React on Rails applications, Ruby on Rails is the backend. Rather than bypassing Rails to access the database directly from Server Components, React on Rails Pro provides ",e.createElement(a.strong,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}))," -- a streaming view helper that 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."),"\n",e.createElement(a.p,null,"For ordinary Rails-provided props, pass the data through the helper's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," option. For slow or independent props that should resolve behind Suspense boundaries, use the async-props helper variant, ",e.createElement(a.strong,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})),". The helper block receives an emitter; call ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call(prop_name, value)</code>'}})," as each async prop becomes available. The Server Component reads emitted values with the injected ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}})," prop."),"\n",e.createElement(a.p,null,"This is the recommended data fetching pattern for React on Rails because:"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,"It preserves Rails' controller/model/view architecture"),"\n",e.createElement(a.li,null,"It leverages Rails' existing data access layers (ActiveRecord, authorization, caching)"),"\n",e.createElement(a.li,null,"It supports streaming SSR — HTML streams to the browser as React renders"),"\n",e.createElement(a.li,null,"Data passes through Rails-provided props or async props -- no client-side fetching needed"),"\n"),"\n",e.createElement(a.div,{className:"markdown-alert markdown-alert-note",dir:"auto"},"\n",e.createElement(a.p,{className:"markdown-alert-title",dir:"auto"},e.createElement(a.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},e.createElement(a.path,{d:"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"})),"NOTE"),"\n",e.createElement(a.p,null,"Server Components rendered through the node renderer do not automatically receive host Node.js globals such as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">fetch</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Headers</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Request</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Response</code>'}}),", ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">AbortController</code>'}}),", or ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">AbortSignal</code>'}}),". To make HTTP calls from a Server Component:"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,e.createElement(a.strong,null,"Prefer props"),": pass Rails-owned data from the controller and avoid HTTP entirely."),"\n",e.createElement(a.li,null,e.createElement(a.strong,null,"Bundle an HTTP client"),": import ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">node-fetch</code>'}})," v2 (CJS) or a compatible ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">undici</code>'}})," version in component code so the bundler includes it."),"\n",e.createElement(a.li,null,e.createElement(a.strong,null,"Inject via ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">additionalContext</code>'}}
1)),": expose the host's fetch globals or a polyfill at renderer startup."),"\n"),"\n",e.createElement(a.p,null,"See ",e.createElement(a.a,{href:"../building-features/node-renderer/js-configuration.md#runtime-globals-for-ssr-and-rsc"},"Node Renderer Runtime Globals"),"."),"\n"),"\n",e.createElement(a.h3,{id:"how-streaming-works",style:{position:"relative"}},e.createElement(a.a,{href:"#how-streaming-works","aria-label":"how streaming works 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>'}})),"How Streaming Works"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Rails view (ERB), synchronous 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"> 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">name</span><span class="token operator">:</span> product<span class="token punctuation">.</span>name<span class="token punctuation">,</span>\n               <span class="token symbol">price</span><span class="token operator">:</span> product<span class="token punctuation">.</span>price<span class="token punctuation">,</span>\n               <span class="token symbol">reviews</span><span class="token operator">:</span> product<span class="token punctuation">.</span>reviews\n                          <span class="token punctuation">.</span>as_json<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><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Rails view (ERB), async 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"> stream_react_component_with_async_props<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">name</span><span class="token operator">:</span> product<span class="token punctuation">.</span>name<span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> product<span class="token punctuation">.</span>price <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>emit<span class="token operator">|</span>\n  emit<span class="token punctuation">.</span>call<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"reviews"</span></span><span class="token punctuation">,</span> product<span class="token punctuation">.</span>reviews<span class="token punctuation">.</span>as_json<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>\n<span class="token keyword">end</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.div,{className:"markdown-alert markdown-alert-important",dir:"auto"},"\n",e.createElement(a.p,{className:"markdown-alert-title",dir:"auto"},e.createElement(a.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},e.createElement(a.path,{d:"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"})),"IMPORTANT"),"\n",e.createElement(a.p,null,"The emitter block runs normal Ruby code sequentially, so ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}})," does ",e.createElement(a.strong,null,"not")," parallelize slow queries by itself. For independent slow data sources, start the work concurrently before emitting values; see ",e.createElement(a.a,{href:"#avoiding-server-side-waterfalls"},"Avoiding Server-Side Waterfalls"),"."),"\n"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"See also:")," ",e.createElement(a.a,{href:"/linked-assets/10232e106e5fc0ce4f959061f43f1b3e/streaming-ssr.md"},"React on Rails Pro streaming SSR")," for setup instructions and configuration options."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React component for synchronous props (Server Component):")),"\n",e.createElement(a.p,null,"Use this version with the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," ERB helper above. Rails resolves every prop before rendering begins, including ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">reviews</code>'}}),"."),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token keyword">type</span> <span class="token class-name">Review</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n  text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n  rating<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">ProductPageProps</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n  price<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n  reviews<span class="token operator">:</span> Review<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token 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 punctuation">{</span> name<span class="token punctuation">,</span> price<span class="token punctuation">,</span> reviews <span class="token punctuation">}</span><span class="token operator">:</span> ProductPageProps<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>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 plain-text">$</span><span class="token punctuation">{</span>price<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">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>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>\n  <span class="token punctuation">)</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">function</span> <span class="token function">ReviewList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> reviews <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> reviews<span class="token operator">:</span> Review<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  <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>reviews<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>review<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>review<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>review<span class="token punctuation">.</span>text<span class="token punctuation">}</span><span class="token plain-text"> (</span><span class="token punctuation">{</span>review<span class="token punctuation">.</span>rating<span class="token punctuation">}</span><span class="token plain-text">/5)\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,"React component for async props (Server Component):")),"\n",e.createElement(a.p,null,"Use this version with the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," ERB helper above. Keep the registered component name as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ProductPage</code>'}}),"; the props shape changes so ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">reviews</code>'}})," comes from ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}}),". ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">WithAsyncProps&lt;AsyncProps, SyncProps></code>'}})," combines the props passed directly through ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," with a typed accessor for the keys emitted from Rails.\nThis first async-props snippet is intentionally single-prop so it mirrors the synchronous example; the full two-prop version appears in ",e.createElement(a.a,{href:"#async-props-stream-each-slow-prop-independently"},"Async Props"),"."),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token keyword">import</span> <span class="token punctuation">{</span> Suspense <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 keyword">type</span> <span class="token punctuation">{</span> WithAsyncProps <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Review</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n  text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n  rating<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">SyncProps</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n  price<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">AsyncProps</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  reviews<span class="token operator">:</span> Review<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">ProductPageProps</span> <span class="token operator">=</span> WithAsyncProps<span class="token operator">&lt;</span>AsyncProps<span class="token punctuation">,</span> SyncProps<span class="token operator">></span><span class="token punctuation">;</span>\n\n<span class="token keyword">function</span> <span class="token function">ReviewList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> reviews <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> reviews<span class="token operator">:</span> Review<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  <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>reviews<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>review<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>review<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>review<span class="token punctuation">.</span>text<span class="token punctuation">}</span><span class="token plain-text"> (</span><span class="token punctuation">{</span>review<span class="token punctuation">.</span>rating<span class="token punctuation">}</span><span class="token plain-text">/5)\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>\n\n<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">AsyncReviewList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> reviewsPromise <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> reviewsPromise<span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Review<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token comment">// This awaits a Promise injected by getReactOnRailsAsyncProp, not a direct data fetch.</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">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><span class="token keyword">await</span> reviewsPromise<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span>\n<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 punctuation">{</span> name<span class="token punctuation">,</span> price<span class="token punctuation">,</span> getReactOnRailsAsyncProp <span class="token punctuation">}</span><span class="token operator">:</span> ProductPageProps<span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> reviewsPromise <span class="token operator">=</span> <span class="token function">getReactOnRailsAsyncProp</span><span class="token punctuation">(</span><span class="token string">\'reviews\'</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>h1</span><span class="token punctuation">></span></span><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 plain-text">$</span><span class="token punctuation">{</span>price<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">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><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">Loading reviews...</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></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">AsyncReviewList</span></span> <span class="token attr-name">reviewsPromise</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>reviewsPromise<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">Suspense</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.div,{className:"markdown-alert markdown-alert-important",dir:"auto"},"\n",e.createElement(a.p,{className:"markdown-alert-title",dir:"auto"},e.createElement(a.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},e.createElement(a.path,{d:"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"})),"IMPORTANT"),"\n",e.createElement(a.p,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," is the loading boundary, not the error UI. If the async-props stream closes before a requested prop is emitted, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}})," rejects and follows the same RSC/streaming error path as other Server Component render errors. Use the page-level handling described 
1in ",e.createElement(a.a,{href:"./rsc-troubleshooting.md#error-boundary-limitations"},"Error Boundary Limitations")," rather than treating the Suspense fallback as failure UI."),"\n"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"How it works:")),"\n",e.createElement(a.ol,null,"\n",e.createElement(a.li,null,"Rails evaluates synchronous ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," for ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}),", or passes those synchronous ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," to ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," while the block emits slower values with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}})),"\n",e.createElement(a.li,null,"The streaming helper uses React's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">renderToPipeableStream</code>'}})," for streaming SSR"),"\n",e.createElement(a.li,null,e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">getReactOnRailsAsyncProp('reviews')</code>"}})," returns a Promise that resolves when Rails calls ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call("reviews", ...)</code>'}})),"\n",e.createElement(a.li,null,"HTML streams to the browser as React renders the component tree"),"\n",e.createElement(a.li,null,"No client-side fetching or ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}}),"-based loading state needed"),"\n",e.createElement(a.li,null,"The component renders with zero JavaScript cost as a Server Component"),"\n"),"\n",e.createElement(a.p,null,"With async props, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," starts rendering with the synchronous ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," values, then the block emits slow values with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}}),". The Server Component uses ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}})," to obtain those values as Promises and places them behind Suspense boundaries."),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"HTML streaming vs. progressive data streaming:")," With synchronous props, all data is loaded in Rails before rendering begins. The streaming here is ",e.createElement(a.em,null,"HTML streaming")," — React sends rendered HTML to the browser as it processes the component tree, rather than waiting for the entire page to finish rendering. For progressive data streaming where slow data sources resolve independently via Suspense boundaries, see ",e.createElement(a.a,{href:"#async-props-stream-each-slow-prop-independently"},"Async props")," below; for the underlying SSR setup (Rack middleware, streaming controller), see ",e.createElement(a.a,{href:"/linked-assets/10232e106e5fc0ce4f959061f43f1b3e/streaming-ssr.md"},"Streaming SSR"),"."),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"More details:")," For setup instructions and configuration options, see the ",e.createElement(a.a,{href:"/linked-assets/37d1c89f8266fd0f03818d68f8999302/tutorial.md"},"React on Rails Pro RSC documentation"),"."),"\n"),"\n",e.createElement(a.h3,{id:"async-props-stream-each-slow-prop-independently",style:{position:"relative"}},e.createElement(a.a,{href:"#async-props-stream-each-slow-prop-independently","aria-label":"async props stream each slow prop independently 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>'}})),"Async Props: Stream Each Slow Prop Independently"),"\n",e.createElement("span",{id:"async-props-stream-each-slow-prop-independently"}),"\n",e.createElement(a.p,null,"The synchronous example above loads every prop in the controller before React starts rendering. When one data source is slow (a recommendations service, an expensive aggregate), it holds up the whole render. ",e.createElement(a.strong,null,"Async props")," let Rails send the fast props immediately and stream each slow prop to the browser as it resolves, so React shows the shell and fills in each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," boundary independently."),"\n",e.createElement(a.img,{src:"/l
1inked-assets/24f9ef7108ca62c8c2882d153659e284/async-props-execution.svg",alt:"How async props work in React on Rails"}),"\n",e.createElement(a.p,null,"Use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," and emit each slow prop from the block. The fast props go in ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}}),"; each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call(name, value)</code>'}})," streams one more prop as soon as Rails has it:"),"\n",e.createElement(a.div,{className:"markdown-alert markdown-alert-note",dir:"auto"},"\n",e.createElement(a.p,{className:"markdown-alert-title",dir:"auto"},e.createElement(a.svg,{className:"octicon",viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true"},e.createElement(a.path,{d:"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"})),"NOTE"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Prerequisites:")," the controller must ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">include ReactOnRailsPro::Stream</code>'}})," and render the view via ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_view_containing_react_components</code>'}}),", and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">config.enable_rsc_support = true</code>'}})," must be set in your React on Rails initializer. If either prerequisite is missing, the helpers raise explicit setup errors: ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," raises ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRailsPro::Error</code>'}})," when ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">config.enable_rsc_support</code>'}})," is false, and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">consumer_stream_async</code>'}})," raises ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails::Error</code>'}})," when ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_view_containing_react_components</code>'}})," was not called."),"\n"),"\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"> stream_react_component_with_async_props<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">name</span><span class="token operator">:</span> product<span class="token punctuation">.</span>name<span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> product<span class="token punctuation">.</span>price <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>emit<span class="token operator">|</span>\n  <span class="token comment"># Each emit.call streams a prop to the browser the moment Rails has it.</span>\n  emit<span class="token punctuation">.</span>call<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"reviews"</span></span><span class="token punctuation">,</span> product<span class="token punctuation">.</span>reviews<span class="token punctuation">.</span>as_json<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>\n  emit<span class="token punctuation">.</span>call<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"recommendations"</span></span><span class="token punctuation">,</span>\n            product<span class="token punctuation">.</span>recommended_products<span class="token punctuation">.</span>as_json<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">
1:name</span><span class="token punctuation">,</span> <span class="token symbol">:price</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>\n<span class="token keyword">end</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.p,null,"On the React side, the component receives a ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">getReactOnRailsAsyncProp</code>'}})," helper alongside its synchronous props. Calling it returns a Promise for that prop; wrap the consumer in ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," so React streams it in when Rails emits it:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token keyword">import</span> <span class="token punctuation">{</span> Suspense <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 keyword">type</span> <span class="token punctuation">{</span> WithAsyncProps <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'react-on-rails-pro\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Review</span> <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> rating<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token keyword">type</span> <span class="token class-name">Product</span> <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> price<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">SyncProps</span> <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> price<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// AsyncProps lists the *resolved* prop types. WithAsyncProps wraps each in a</span>\n<span class="token comment">// Promise at the call site, so getReactOnRailsAsyncProp(\'reviews\') returns</span>\n<span class="token comment">// Promise&lt;Review[]> — which is then forwarded to an async child that awaits it.</span>\n<span class="token keyword">type</span> <span class="token class-name">AsyncProps</span> <span class="token operator">=</span> <span class="token punctuation">{</span> reviews<span class="token operator">:</span> Review<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> recommendations<span class="token operator">:</span> Product<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">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 punctuation">{</span>\n  name<span class="token punctuation">,</span>\n  price<span class="token punctuation">,</span>\n  getReactOnRailsAsyncProp<span class="token punctuation">,</span>\n<span class="token punctuation">}</span><span class="token operator">:</span> WithAsyncProps<span class="token operator">&lt;</span>AsyncProps<span class="token punctuation">,</span> SyncProps<span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> reviewsPromise <span class="token operator">=</span> <span class="token function">getReactOnRailsAsyncProp</span><span class="token punctuation">(</span><span class="token string">\'reviews\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span>
1 recommendationsPromise <span class="token operator">=</span> <span class="token function">getReactOnRailsAsyncProp</span><span class="token punctuation">(</span><span class="token string">\'recommendations\'</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>h1</span><span class="token punctuation">></span></span><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 plain-text">$</span><span class="token punctuation">{</span>price<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\n      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><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 plain-text">Loading reviews…</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></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">AsyncReviewList</span></span> <span class="token attr-name">reviewsPromise</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>reviewsPromise<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">Suspense</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">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><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 plain-text">Loading recommendations…</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></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">AsyncRecommendationList</span></span> <span class="token attr-name">itemsPromise</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>
1recommendationsPromise<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">Suspense</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">// Each async child awaits only the prop it was handed.</span>\n<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">AsyncReviewList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> reviewsPromise <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> reviewsPromise<span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Review<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> resolved <span class="token operator">=</span> <span class="token keyword">await</span> reviewsPromise<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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>resolved<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>r<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>r<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>r<span class="token punctuation">.</span>text<span class="token punctuation">}</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>\n\n<span class="token comment">// AsyncRecommendationList mirrors AsyncReviewList: it awaits the recommendations Promise.</span>\n<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">AsyncRecommendationList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> itemsPromise <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> itemsPromise<span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Product<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> resolved <span class="token operator">=</span> <span class="token keyword">await</span> itemsPromise<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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>resolved<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>p<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>p<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>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.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Production note:")," Wrap each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," in an ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;ErrorBoundary></code>'}})," so that if an async-prop stream rejects (e.g. a Rails-side exception while emitting), the boundary degrades to a fallback instead of crashing the whole page. See the ",e.createElement(a.a,{href:"../../pro/react-server-components/inside-client-components.md#error-handling"},"error-handling pattern")," for a reusable boundary."),"\n"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Sync props vs. async props — which to use:")),"\n",e.createElement(a.table,null,e.createElement(a.thead,null,e.createElement(a.tr,null,e.createElement(a.th),e.createElement(a.th,null,"Sync props (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}),")"),e.createElement(a.th,null,"Async props (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),")"))),e.createElement(a.tbody,null,e.createElement(a.tr,null,e.createElement(a.td,null,"When Rails has the data"),e.createElement(a.td,null,"All props loaded before rendering begins"),e.createElement(a.td,null,"Fast props now; each slow prop streamed as it resolves")),e.createElement(a.tr,null,e.createElement(a.td,null,"What streams"),e.createElement(a.td,null,"Rendered HTML, as React walks the tree"),e.createElement(a.td,null,"Rendered HTML ",e.createElement(a.strong,null,"plus")," each prop independently")),e.createElement(a.tr,null,e.createElement(a.td,null,"Best for"),e.createElement(a.td,null,"All data sources are fast"),e.createElement(a.td,null,"One or more data sources are slow")))),"\n",e.createElement(a.p,null,"Async props keep Rails as the backend: Rails still owns the queries, authorization, and caching — the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit</code>'}})," block is ordinary Rails code running in the streaming view helper, not the React component. It just emits each result the moment it has it instead of blocking the whole render on the slowest source. (Referencing controller instance variables like ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@product</code>'}})," from the block is fine and normal — the constraint is narrower: don't ",e.createElement(a.em,null,"pre-resolve")," the slow emit-block queries in the controller action, because it runs to completion before the view streams, so doing that work up front defeats the progressive streaming.) Requires React Server Components (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">config.enable_rsc_support = true</code>'}}),")."),"\n",e.createElement(a.h4,{id:"parallelize-the-queries-with-the-async-gem",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#parallelize-the-queries-with-the-async-gem","aria-label":"parallelize the queries with the async gem 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>'}})),"Parallelize the queries with the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async</code>'}})," gem"),"\n",e.createElement(a.p,null,"In the block above, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">reviews</code>'}})," is emitted before ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">recommendations</code>'}})," — the second query doesn't start until the first ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}})," returns, so their times add up. The block already runs inside an ",e.createElement(a.a,{href:"https://github.com/socketry/async"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async</code>'}}))," reactor (the same one the Pro renderer uses for its HTTP/2 stream), so you can fan the independent queries out into concurrent tasks and emit each prop the moment its own query resolves:"),"\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"> stream_react_component_with_async_props<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">name</span><span class="token operator">:</span> product<span class="token punctuation">.</span>name<span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> product<span class="token punctuation">.</span>price <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>emit<span class="token operator">|</span>\n  <span class="token comment"># The block is already running inside an Async reactor. `Sync` reuses it (or</span>\n  <span class="token comment"># starts one if none exists — either way no new OS thread) and acts as a</span>\n  <span class="token comment"># synchronization barrier: it runs the block with the current task (`parent`)</span>\n  <span class="token comment"># and returns only after every child started via `parent.async` has finished</span>\n  <span class="token comment"># — exactly when the stream should close.</span>\n  Sync <span class="token keyword">do</span> <span class="token operator">|</span>parent<span class="token operator">|</span>\n    parent<span class="token punctuation">.</span>async <span class="token keyword">do</span>\n      <span class="token comment"># Each concurrent fiber checks out its OWN connection for the duration of</span>\n      <span class="token comment"># its query, then releases it before emitting (see the note below).</span>\n      reviews <span class="token operator">=</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base<span class="token punctuation">.</span>connection_pool<span class="token punctuation">.</span>with_connection <span class="token keyword">do</span>\n        product<span class="token punctuation">.</span>reviews<span class="token punctuation">.</span>as_json<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>
1\n      <span class="token keyword">end</span>\n      emit<span class="token punctuation">.</span>call<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"reviews"</span></span><span class="token punctuation">,</span> reviews<span class="token punctuation">)</span>\n    <span class="token keyword">end</span>\n\n    parent<span class="token punctuation">.</span>async <span class="token keyword">do</span>\n      recommendations <span class="token operator">=</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base<span class="token punctuation">.</span>connection_pool<span class="token punctuation">.</span>with_connection <span class="token keyword">do</span>\n        product<span class="token punctuation">.</span>recommended_products<span class="token punctuation">.</span>as_json<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">:name</span><span class="token punctuation">,</span> <span class="token symbol">:price</span><span class="token punctuation">]</span><span class="token punctuation">)</span>\n      <span class="token keyword">end</span>\n      emit<span class="token punctuation">.</span>call<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"recommendations"</span></span><span class="token punctuation">,</span> recommendations<span class="token punctuation">)</span>\n    <span class="token keyword">end</span>\n  <span class="token keyword">end</span>\n<span class="token keyword">end</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.p,null,"Both tasks follow the same shape — wrap each query in its own ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">with_connection</code>'}}),", then emit. (If a prop comes from an external service over a fiber-aware HTTP client rather than the database, that task skips ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">with_connection</code>'}})," since it never touches the connection pool.) Each child task emits on its own, so props arrive in whatever order they resolve and React fills each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," boundary as its prop lands — the fastest source paints first and the total time is roughly the slowest source instead of the sum of all of them."),"\n",e.createElement(a.p,null,"Calling ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}})," from concurrent fibers is safe: the reactor is single-threaded and cooperatively scheduled, and each ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">emit.call</code>'}})," writes one complete NDJSON line in a single operation — so concurrent emits never interleave within a line. Their ",e.createElement(a.em,null,"order")," can vary (whichever query resolves first emits first), which is fine because each line is a self-contained prop update."),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"When this actually runs in parallel:")," the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async</code>'}})," gem parallelizes ",e.createElement(a.strong,null,"I/O-bound")," work that yields to the fiber scheduler — most reliably calls to external services through a fiber-aware HTTP client (the renderer already depends on ",e.createElement(a.a,{href:"https://github.com/socketry/async-http"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async-http</code>'}})),"). For ActiveRecord, give each concurrent fiber its own connection with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ActiveRecord::Base.connection_pool.with_connection</code>'}}),", run with fiber-based connection isolation (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">config.active_support.isolation_level = :fiber</code>'}}),", Rails 7.1+), and size the pool for the fan-out — otherwise the fiber
1s share one connection and the queries serialize. (On Rails 6.x–7.0 the connection pool ignores fiber identity, so the queries serialize with no visible error — or worse, corrupt results under concurrent load.) For CPU-bound work, or a database driver that doesn't cooperate with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Fiber.scheduler</code>'}}),", parallelize with threads instead (see ",e.createElement(a.a,{href:"#avoiding-server-side-waterfalls"},"Avoiding Server-Side Waterfalls"),")."),"\n",e.createElement(a.p,null,"See ",e.createElement(a.a,{href:"/linked-assets/98f9061f8396f36af9d98f9d2223fe22/async-props-database-queries.md"},"Database Queries in Async Props Blocks")," for the complete configuration guide covering isolation level, pool sizing, driver compatibility, connection lifecycle (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">with_connection</code>'}})," vs ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">lease_connection</code>'}}),"), ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">CurrentAttributes</code>'}})," behavior, and troubleshooting. If fiber scheduling isn't configured, queries fall back to running sequentially — but with multiple async-props components on one page, the default ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">isolation_level = :thread</code>'}})," can cause silent connection corruption rather than graceful serialization."),"\n"),"\n",e.createElement(a.h2,{id:"migrating-from-react-query--tanstack-query",style:{position:"relative"}},e.createElement(a.a,{href:"#migrating-from-react-query--tanstack-query","aria-label":"migrating from react query  tanstack query 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>'}})),"Migrating from React Query / TanStack Query"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"New to TanStack Query on Rails?")," This section covers ",e.createElement(a.em,null,"migrating")," an existing React Query setup into RSC. For a from-scratch guide to the recommended patterns (CSRF-aware fetch, stable query keys, first-paint seeding, and mutations), see ",e.createElement(a.a,{href:"/linked-assets/cdaac217e38025596a9992f7eb882fd9/tanstack-query.md"},"Using TanStack Query"),"."),"\n"),"\n",e.createElement(a.p,null,"React Query remains valuable in the RSC world for features like polling, optimistic updates, and infinite scrolling. But for simple data display, Server Components replace it entirely."),"\n",e.createElement(a.h3,{id:"pattern-1-simple-replacement-no-client-cache-needed",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-1-simple-replacement-no-client-cache-needed","aria-label":"pattern 1 simple replacement no client cache needed 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: Simple Replacement (No Client Cache Needed)"),"\n",e.createElement(a.p,null,"If a component only displays data without mutations, polling, or optimistic updates, replace React Query with a Server Component:"),"\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">// Before: React Query</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> useQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'@tanstack/react-query\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">function</span> <span class="token function">ProductList</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> data<span class="token punctuation">,</span> isLoading<span class="token punctuation">,</span> error <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>\n    <span class="token literal-property property">queryKey</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">\'products\'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n    <span class="token function-variable function">queryFn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">\'/api/products\'</span><span class="token punctuation">)</span><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><span class="token punctuation">,</span>\n  <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>isLoading<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">Spinner</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span>\n  <span class="token keyword">if</span> <span class="token punctuation">(</span>error<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">Error</span></span> <span class="token attr-name">message</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>error<span class="token punctuation">.</span>message<span class="token punctuation">}</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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>data<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">p</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>p<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>p<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>li</span><span class="token punctuation">></span></span>
1\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.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">"ProductList"</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">products</span><span class="token operator">:</span> Product<span class="token punctuation">.</span>limit<span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">.</span>as_json<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">:name</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">// After: Server Component -- receives data from Rails controller props</span>\n<span class="token keyword">function</span> <span class="token function">ProductList</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> products <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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>products<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">p</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>p<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>p<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>li</span><span class="token punctuation">></span></span>
1\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.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React on Rails note:")," In React on Rails, the controller prepares the data and passes it as props -- no ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async/await</code>'}})," in the component, no direct data layer calls. For data that's slow to compute, use ",e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}))," to stream it in progressively with Suspense. The generic ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">async function</code>'}})," + ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">await</code>'}})," pattern shown in other RSC frameworks bypasses Rails' authorization and caching layers and is not recommended."),"\n"),"\n",e.createElement(a.h3,{id:"pattern-2-rails-props-as-initial-data-keep-react-query-for-client-features",style:{position:"relative"}},e.createElement(a.a,{href:"#pattern-2-rails-props-as-initial-data-keep-react-query-for-client-features","aria-label":"pattern 2 rails props as initial data keep react query for client features 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: Rails Props as Initial Data (Keep React Query for Client Features)"),"\n",e.createElement(a.p,null,"When you need React Query's client features (background refetching, mutations, optimistic updates), pass Rails controller props as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">initialData</code>'}})," so the component renders instantly with server data, then React Query takes over for client-side updates:"),"\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">// ReactQueryProvider.jsx -- Client Component (provides QueryClient)</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> QueryClient<span class="token punctuation">,</span> QueryClientProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'@tanstack/react-query\'</span><span class="token punctuation">;</span>\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">ReactQueryProvider</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>queryClient<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 operator">=></span> <span class="token keyword">new</span> <span class="token class-name">QueryClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">QueryClientProvider</span></span> <span class="token attr-name">client</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>queryClient<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><span class="token class-name">QueryClientProvider</span></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">// ProductsPage.jsx -- Server Component (receives data from Rails controller props)</span>\n<span class="token keyword">
1import</span> ReactQueryProvider <span class="token keyword">from</span> <span class="token string">\'./ReactQueryProvider\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> ProductList <span class="token keyword">from</span> <span class="token string">\'./ProductList\'</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">ProductsPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> products <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><span class="token class-name">ReactQueryProvider</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">ProductList</span></span> <span class="token attr-name">initialProducts</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>products<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">ReactQueryProvider</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.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// ProductList.jsx -- Client Component (uses React Query hooks)</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> useQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'@tanstack/react-query\'</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">ProductList</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> initialProducts <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> <span class="token literal-property property">data</span><span class="token operator">:</span> products <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>\n    <span class="token literal-property property">queryKey</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">\'products\'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n    <span class="token function-variable function">queryFn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">\'/api/products\'</span><span class="token punctuation">)</span><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><span class="token punctuation">,</span>\n    <span class="token literal-property property">initialData</span><span class="token operator">:</span> initialProducts<span class="token punctuation">,</span>\n    <span class="token literal-property property">initialDataUpdatedAt</span><span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// Marks the data as fresh as of client render time</span>\n    <span class="token literal-property property">staleTime</span><span class="token operator">:</span> <span class="token number">5</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">,</span> <span class="token comment">// Treat Rails-fetched data as fresh for 5 min</span>\n  <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>products<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">p</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>p<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>p<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token plain-text"> - $</span><span class="token punctuation">{</span>p<span class="token punctuation">.</span>price<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>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.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">"ProductsPage"</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">products</span><span class="token operator">:</span> Product<span class="token punctuation">.</span>limit<span class="token punctuation">(</span><span class="token number">50</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.p,null,e.createElement(a.strong,null,"How it works:")),"\n",e.createElement(a.ol,null,"\n",e.createElement(a.li,null,"Rails controller fetches products and passes them as props"),"\n",e.createElement(a.li,null,"Server Component passes the data to the Client Component as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">initialProducts</code>'}})),"\n",e.createElement(a.li,null,"React Query uses ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">initialData</code>'}})," to populate the cache with no loading state on first render"),"\n",e.createElement(a.li,null,"Subsequent refetches happen client-side as usual"),"\n"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Note:")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">initialDataUpdatedAt</code>'}})," and ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">staleTime</code>'}})," work together to prevent React Query from treating the Rails data as immediately stale on mount. ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Date.now()</code>'}})," uses the client render timestamp, not the actual Rails fetch time — this is close enough for most apps. For precise control, pass a timestamp from your Rails controller (e.g., ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">(Time.now.to_f * 1000).to_i</code>'}}),") as a prop and use that instead. If you don't need timed refetching at all, use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">staleTime: Infinity</code>'}})," to prevent automatic refetches entirely."),"\n"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Alternative:")," For complex cases with many queries, you can use TanStack Query's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">dehydrate</code>'}}),"/",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">HydrationBoundary</code>'}})," pattern to prefetch and seed the entire QueryClient cache on the server. See the ",e.createElement(a.a,{href:"https://tanstack.com/query/latest/docs/framework/react/guides/ssr"},"TanStack Query SSR docs")," for details."),"\n"),"\n",e.createElement(a.h2,{id:"migrating-from-swr",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#migrating-from-swr","aria-label":"migrating from swr 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>'}})),"Migrating from SWR"),"\n",e.createElement(a.p,null,"SWR follows a similar pattern -- pass Rails controller props as ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">fallbackData</code>'}})," so the component renders instantly with server data:"),"\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">// DashboardPage.jsx -- Server Component (receives data from Rails controller props)</span>\n<span class="token keyword">import</span> DashboardStats <span class="token keyword">from</span> <span class="token string">\'./DashboardStats\'</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">DashboardPage</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 tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">DashboardStats</span></span> <span class="token attr-name">fallbackData</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 punctuation">;</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\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">"DashboardPage"</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">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><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">// DashboardStats.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> useSWR <span class="token keyword">from</span> <span class="token string">\'swr\'</span><span class="token punctuation">;</span>\n\n<span class="token keyword">const</span> <span class="token function-variable function">fetcher</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">url</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><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><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">DashboardStats</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> fallbackData <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> <span class="token literal-property property">data</span><span class="token operator">:</span> stats <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSWR</span><span class="token punctuation">(</span><span class="token string">\'/api/dashboard/stats\'</span><span class="token punctuation">,</span> fetcher<span class="token punctuation">,</span> <span class="token punctuation">{</span>\n    fallbackData<span class="token punctuation">,</span>\n  <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>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.h2,{id:"avoiding-server-side-waterfalls",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#avoiding-server-side-waterfalls","aria-label":"avoiding server side waterfalls 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>'}})),"Avoiding Server-Side Waterfalls"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React on Rails note:")," In React on Rails, use ",e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}))," when slow Rails data should stream into Suspense boundaries independently. If independent values require slow Ruby work, start that work concurrently before emitting; the patterns below apply when you have async Server Components that fetch data directly (outside the async props flow)."),"\n"),"\n",e.createElement(a.p,null,"The most critical performance pitfall with Server Components is sequential data fetching. When one ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">await</code>'}})," blocks the next, you create a waterfall on the server:"),"\n",e.createElement(a.h3,{id:"the-problem-sequential-queries",style:{position:"relative"}},e.createElement(a.a,{href:"#the-problem-sequential-queries","aria-label":"the problem sequential queries 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: Sequential Queries"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ruby"><pre class="language-ruby"><code class="language-ruby"><span class="token comment"># BAD: Each query blocks the next (750ms total)</span>\n<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span>\n  <span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:user_id</span><span class="token punctuation">]</span><span class="token punctuation">)</span>          <span class="token comment"># 200ms</span>\n  <span class="token variable">@stats</span> <span class="token operator">=</span> DashboardStats<span class="token punctuation">.</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token variable">@user</span><span class="token punctuation">)</span>           <span class="token comment"># 300ms (waits for user)</span>\n  <span class="token variable">@posts</span> <span class="token operator">=</span> <span class="token variable">@user</span><span class="token punctuation">.</span>posts<span class="token punctuation">.</span>recent                  <span class="token comment"># 250ms (sequential)</span>\n  stream_view_containing_react_components<span class="token punctuation">(</span><span class="token symbol">template</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"dashboard/show"</span></span><span class="token punctuation">)</span>\n<span class="token keyword">end</span></code></pre></div>'}}),"\n",e.createElement(a.h3,{id:"solution-1-parallelize-independent-queries",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#solution-1-parallelize-independent-queries","aria-label":"solution 1 parallelize independent queries 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>'}})),"Solution 1: Parallelize Independent Queries"),"\n",e.createElement(a.p,null,"When data sources are independent, use Ruby threads to fetch in parallel:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ruby"><pre class="language-ruby"><code class="language-ruby"><span class="token comment"># GOOD: Fetch in parallel (300ms -- limited by slowest)</span>\n<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span>\n  user_id <span class="token operator">=</span> params<span class="token punctuation">[</span><span class="token symbol">:user_id</span><span class="token punctuation">]</span>\n  results <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>\n  threads <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>\n  threads <span class="token operator">&lt;&lt;</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token keyword">do</span>\n    ActiveRecord<span class="token double-colon punctuation">::</span>Base<span class="token punctuation">.</span>connection_pool<span class="token punctuation">.</span>with_connection <span class="token keyword">do</span>\n      results<span class="token punctuation">[</span><span class="token symbol">:user</span><span class="token punctuation">]</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span>user_id<span class="token punctuation">)</span><span class="token punctuation">.</span>as_json\n    <span class="token keyword">end</span>\n  <span class="token keyword">end</span>\n  threads <span class="token operator">&lt;&lt;</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token keyword">do</span>\n    ActiveRecord<span class="token double-colon punctuation">::</span>Base<span class="token punctuation">.</span>connection_pool<span class="token punctuation">.</span>with_connection <span class="token keyword">do</span>\n      results<span class="token punctuation">[</span><span class="token symbol">:stats</span><span class="token punctuation">]</span> <span class="token operator">=</span> DashboardStats<span class="token punctuation">.</span>compute<span class="token punctuation">.</span>as_json\n    <span class="token keyword">end</span>\n  <span class="token keyword">end</span>\n  threads <span class="token operator">&lt;&lt;</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token keyword">do</span>\n    ActiveRecord<span class="token double-colon punctuation">::</span>Base<span class="token punctuation">.</span>connection_pool<span class="token punctuation">.</span>with_connection <span class="token keyword">do</span>\n      results<span class="token punctuation">[</span><span class="token symbol">:posts</span><span class="token punctuation">]</span> <span class="token operator">=</span> Post<span class="token punctuation">.</span>recent<span class="token punctuation">.</span>as_json\n    <span class="token keyword">end</span>\n  <span class="token keyword">end</span>\n  threads<span class="token punctuation">.</span><span class="token keyword">each</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token symbol">:join</span><span class="token punctuation">)</span>\n  <span class="token variable">@dashboard_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">"My Dashboard"</span></span> <span class="token punctuation">}</span><span class="token punctuation">.</span>merge<span class="token punctuation">(</span>results<span class="token punctuation">)</span>\n  stream_view_containing_react_components<span class="token punctuation">(</span><span class="token symbol">template</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"dashboard/show"</span></span><span class="token punctuation">)</span>\n<span class="token keyword">end</span></code></pre></div>'}}),"\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"># All data fetched in parallel, rendered with streaming SSR </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> <span class="token symbol">props</span><span class="token operator">:</span> <span class="token variable">@dashboard_props</span><span class="token punctuation">)</span> </span><span class="token delimiter punctuation">%></span></span></code></pre></div>'}}),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Note:")," In production, wrap each thread body in a ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">rescue</code>'}})," to avoid incomplete results if a query fails. An unhandled exception in any thread will be re-raised by ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">join</code>'}}),"."),"\n"),"\n",e.createElement(a.h3,{id:"solution-2-separate-components-for-independent-data",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#solution-2-separate-components-for-independent-data","aria-label":"solution 2 separate components for independent data 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>'}})),"Solution 2: Separate Components for Independent Data"),"\n",e.createElement(a.p,null,"For data that is truly independent, render multiple ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," calls. Each component renders as its data becomes available:"),"\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"># Each component renders independently </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">"DashboardHeader"</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> <span class="token string-literal"><span class="token string">"My Dashboard"</span></span> <span class="token punctuation">}</span><span class="token punctuation">)</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">"UserProfile"</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">user</span><span class="token operator">:</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:user_id</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span>as_json<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">:name</span><span class="token punctuation">,</span> <span class="token symbol">:avatar_url</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>\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">"StatsPanel"</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">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><span class="token punctuation">)</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">"PostFeed"</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">posts</span><span class="token operator">:</span> Post<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">// Each component is a simple Server Component</span>\n<span class="token keyword">function</span> <span class="token function">UserProfile</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> user <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>user<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>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">function</span> <span class="token function">StatsPanel</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>\n<span class="token keyword">function</span> <span class="token function">PostFeed</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> posts <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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>posts<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">p</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>p<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>p<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>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.h3,{id:"solution-3-pass-all-data-as-props",style:{position:"relative"}},e.createElement(a.a,{href:"#solution-3-pass-all-data-as-props","aria-label":"solution 3 pass all data 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>'}})),"Solution 3: Pass All Data as Props"),"\n",e.createElement(a.p,null,"Fetch all data in the controller and pass it as props. ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," streams the rendered HTML to the browser via React's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">renderToPipeableStream</code>'}}),":"),"\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"> 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">name</span><span class="token operator">:</span> product<span class="token punctuation">.</span>name<span class="token punctuation">,</span>\n               <span class="token symbol">price</span><span class="token operator">:</span> product<span class="token punctuation">.</span>price<span class="token punctuation">,</span>\n               <span class="token symbol">reviews</span><span class="token operator">:</span> product<span class="token punctuation">.</span>reviews\n                          <span class="token punctuation">.</span>as_json<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>\n               <span class="token symbol">related</span><span class="token operator">:</span> product<span class="token punctuation">.</span>
1recommended_products\n                          <span class="token punctuation">.</span>as_json<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">:name</span><span class="token punctuation">,</span> <span class="token symbol">:price</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 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> name<span class="token punctuation">,</span> price<span class="token punctuation">,</span> reviews<span class="token punctuation">,</span> related <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>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 plain-text">$</span><span class="token punctuation">{</span>price<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">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>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">RelatedProducts</span></span> <span class="token attr-name">products</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>related<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 keyword">function</span> <span class="token function">ReviewList</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> reviews <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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>reviews<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">r</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>
1\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>r<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>r<span class="token punctuation">.</span>text<span class="token punctuation">}</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>\n\n<span class="token keyword">function</span> <span class="token function">RelatedProducts</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> products <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>ul</span><span class="token punctuation">></span></span><span class="token plain-text">\n      </span><span class="token punctuation">{</span>products<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">p</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>p<span class="token punctuation">.</span>id<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>p<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>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,"All data is loaded in Rails 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."),"\n",e.createElement(a.h2,{id:"the-use-hook-for-client-components",style:{position:"relative"}},e.createElement(a.a,{href:"#the-use-hook-for-client-components","aria-label":"the use hook for 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>'}})),"The ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," Hook for Client Components"),"\n",e.createElement(a.p,null,"The ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," hook lets Client Components resolve promises. In React on Rails, data typically arr
1ives as resolved props from Rails, so ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," is most relevant when combining Server Components with client-side data fetching libraries."),"\n",e.createElement(a.h3,{id:"common-use-mistakes-in-client-components",style:{position:"relative"}},e.createElement(a.a,{href:"#common-use-mistakes-in-client-components","aria-label":"common use mistakes 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>'}})),"Common ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," Mistakes in Client Components"),"\n",e.createElement(a.p,null,"Creating a promise inside a Client Component and passing it to ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," triggers this runtime error:"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,'"A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework."')),"\n"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Why it happens:")," React tracks promises passed to ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," by ",e.createElement(a.strong,null,"object reference identity")," across re-renders. On each render, it checks whether the promise is the same object as the previous render. When you create a promise inside a Client Component, every render produces a new promise instance -- React sees a different reference, cannot determine if the result is still valid, and throws."),"\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">// WRONG: Creating a promise inline — new promise every render</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> use <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">function</span> <span class="token function">Comments</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> postId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> comments <span class="token operator">=</span> <span class="token function">use</span><span class="token punctuation">(</span><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/comments/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>postId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">r</span><span class="token punctuation">)</span> <span class="token operator">=></span> r<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><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>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>
1\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 punctuation">{</span>c<span class="token punctuation">.</span>text<span class="token punctuation">}</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.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// WRONG: Variable doesn\'t help — still a new promise every render</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> use <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">function</span> <span class="token function">Comments</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> postId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> promise <span class="token operator">=</span> <span class="token function">getComments</span><span class="token punctuation">(</span>postId<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// New promise object each render</span>\n  <span class="token keyword">const</span> comments <span class="token operator">=</span> <span class="token function">use</span><span class="token punctuation">(</span>promise<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>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 punctuation">{</span>c<span class="token punctuation">.</span>text<span class="token punctuation">}</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.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="jsx"><pre class="language-jsx"><code class="language-jsx"><span class="token comment">// WRONG: useMemo seems to work but is NOT reliable</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">
1import</span> <span class="token punctuation">{</span> use<span class="token punctuation">,</span> useMemo <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">function</span> <span class="token function">Comments</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> postId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> promise <span class="token operator">=</span> <span class="token function">useMemo</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">getComments</span><span class="token punctuation">(</span>postId<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>postId<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token keyword">const</span> comments <span class="token operator">=</span> <span class="token function">use</span><span class="token punctuation">(</span>promise<span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token comment">// React does NOT guarantee useMemo stability. From the docs:</span>\n  <span class="token comment">// "React may choose to \'forget\' some previously memoized values</span>\n  <span class="token comment">//  and recalculate them on next render."</span>\n  <span class="token comment">// If React discards the memoized value, a new promise is created,</span>\n  <span class="token comment">// and use() throws the uncached promise error intermittently.</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"The safe approach -- use a Suspense-compatible library:")),"\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">// CORRECT: Suspense-compatible library (TanStack Query)</span>\n<span class="token string">\'use client\'</span><span class="token punctuation">;</span>\n<span class="token keyword">import</span> <span class="token punctuation">{</span> useSuspenseQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">\'@tanstack/react-query\'</span><span class="token punctuation">;</span>\n\n<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> postId <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> <span class="token literal-property property">data</span><span class="token operator">:</span> comments <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSuspenseQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>\n    <span class="token literal-property property">queryKey</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">\'comments\'</span><span class="token punctuation">,</span> postId<span class="token punctuation">]</span><span class="token punctuation">,</span>\n    <span class="token function-variable function">queryFn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">getComments</span><span class="token punctuation">(</span>postId<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// client-side fetch wrapper</span>\n  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token comment">
1// The library manages promise identity internally —</span>\n  <span class="token comment">// same cache key returns the same promise reference.</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>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 punctuation">{</span>c<span class="token punctuation">.</span>text<span class="token punctuation">}</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.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Rule:")," Never create a raw promise for ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">use()</code>'}})," inside a Client Component. Use a Suspense-compatible library like TanStack Query or SWR that manages promise identity internally."),"\n"),"\n",e.createElement(a.h2,{id:"request-deduplication-with-reactcache",style:{position:"relative"}},e.createElement(a.a,{href:"#request-deduplication-with-reactcache","aria-label":"request deduplication with reactcache 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>'}})),"Request Deduplication with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"React on Rails note:")," In most React on Rails applications, data flows through controller props or ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),", so ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})," is unnecessary. This section applies when Server Components call data-fetching functions directly (for example, from the Node renderer). If you are using ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}}),", repeated calls within the same request already share the same deduped Promise."),"\n"),"\n",e.createElement(a.p,null,"When multiple Server Components need the same data, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})," ensures the fetch happens only once per request:"),"\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">// lib/data.js -- Define at module level</span>\n<span class="token keyword">
1import</span> <span class="token punctuation">{</span> cache <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">const</span> getUser <span class="token operator">=</span> <span class="token function">cache</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">id</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n  <span class="token keyword">return</span> <span class="token keyword">await</span> <span class="token function">fetchUserById</span><span class="token punctuation">(</span>id<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></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">// Navbar.jsx and Sidebar.jsx both import getUser.</span>\n<span class="token comment">// The first call fetches; the second returns the cached result.</span>\n<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">Navbar</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> userId <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getUser</span><span class="token punctuation">(</span>userId<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>nav</span><span class="token punctuation">></span></span><span class="token plain-text">Welcome, </span><span class="token punctuation">{</span>user<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>nav</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.p,null,e.createElement(a.strong,null,"Key properties:")),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,"Cache is scoped to the ",e.createElement(a.strong,null,"current request")," -- no cross-request data leakage"),"\n",e.createElement(a.li,null,"Uses ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Object.is</code>'}})," for argument comparison (pass primitives, not objects)"),"\n",e.createElement(a.li,null,"Must be defined at ",e.createElement(a.strong,null,"module level"),", not inside components"),"\n",e.createElement(a.li,null,"Only works in Server Components"),"\n"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Note:")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})," is only available in React Server Component environments. It is not available in Client Components or non-RSC server rendering (e.g., ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">renderToString</code>'}}),")."),"\n"),"\n",e.createElement(a.p,null,"For most React on Rails applications, you won't need ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})," for data fetching because data flows through Rails controller props. However, ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">React.cache()</code>'}})," is valuable for sharing ",e.createElement(a.strong,null,"computed per-request state")," (like intl instances, feature flag lookups, or auth context) across Server Components without prop drilling. See ",e.createElement(a.a,{href:"/linked-assets/aa75860aa0a259635bf5d19a23f9dd51/per-request-data.md"},"Sharing Per-Request Data in Server Components")," for these patterns."),"\n",e.createElement(a.h2,{id:"mutations-rails-controllers-not-server-actions",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mutations-rails-controllers-not-server-actions","aria-label":"mutations rails controllers not server actions 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>'}})),"Mutations: Rails Controllers, Not Server Actions"),"\n",e.createElement(a.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Important:")," React on Rails does ",e.createElement(a.strong,null,"not")," support Server Actions (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}}),"). Server Actions run on the Node renderer, which is a rendering server -- it has no access to Rails models, sessions, cookies, or CSRF protection. Do not use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}})," in React on Rails applications."),"\n"),"\n",e.createElement(a.p,null,"This is a deliberate, settled design decision, not a temporary gap (see the decision record in\n",e.createElement(a.a,{href:"https://github.com/shakacode/react_on_rails/issues/3867"},"#3867"),"): Rails controllers are the mutation\nlayer, and the ergonomics gap with Next.js Server Actions is being closed by a first-class Rails-native\nbridge -- the ",e.createElement(a.a,{href:"/linked-assets/863c79fd329adfee1612a2644be4628b/forms.md"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useRailsForm</code>'}}))," hook paired with the\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails::Controller::FormResponders</code>'}})," controller concern, ",e.createElement(a.strong,null,"shipped")," via\n",e.createElement(a.a,{href:"https://github.com/shakacode/react_on_rails/issues/3872"},"#3872")," (see ",e.createElement(a.a,{href:"/linked-assets/863c79fd329adfee1612a2644be4628b/forms.md"},"Forms and Mutations"),").\nThe ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">fetch</code>'}})," + CSRF pattern below remains fully supported and is exactly what ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useRailsForm</code>'}})," automates. An optional\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}}),"-shaped authoring syntax that compiles down to the Rails bridge (for Next.js-migration\nfamiliarity only) is a deferred follow-up RFC, tracked in\n",e.createElement(a.a,{href:"https://github.com/shakacode/react_on_rails/issues/3956"},"#3956"),"."),"\n",e.createElement(a.p,null,"All mutations in React on Rails should go through Rails controllers via standard forms or API endpoints:"),"\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">// CommentForm.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<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\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">CommentForm</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> postId <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>content<span class="token punctuation">,</span> setContent<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">\'\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n  <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">handleSubmit</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
1\n    e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">\'/api/comments\'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>\n      <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">\'POST\'</span><span class="token punctuation">,</span>\n      <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>\n        <span class="token string-property property">\'Content-Type\'</span><span class="token operator">:</span> <span class="token string">\'application/json\'</span><span class="token punctuation">,</span>\n        <span class="token string-property property">\'X-CSRF-Token\'</span><span class="token operator">:</span> ReactOnRails<span class="token punctuation">.</span><span class="token function">authenticityToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>\n      <span class="token punctuation">}</span><span class="token punctuation">,</span>\n      <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">comment</span><span class="token operator">:</span> <span class="token punctuation">{</span> content<span class="token punctuation">,</span> postId <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>\n    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Request failed: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>status<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token function">setContent</span><span class="token punctuation">(</span><span class="token string">\'\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <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>form</span> <span class="token attr-name">onSubmit</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>handleSubmit<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>textarea</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>content<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">setContent</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> <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">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">Post Comment</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>form</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="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 </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">"CommentForm"</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">postId</span><span class="token operator">:</span> <span class="token variable">@post</span><span class="token punctuation">.</span>id <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.blockquote,null,"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Note:")," ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ReactOnRails.authenticityToken()</code>'}})," reads the CSRF token from the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;meta name="csrf-token"></code>'}})," tag, which is the standard Rails approach. This avoids duplicating the token in component props."),"\n"),"\n",e.createElement(a.p,null,"This preserves Rails' full controller/model layer -- authentication, authorization, CSRF protection, and validations all work as expected."),"\n",e.createElement(a.h2,{id:"when-to-keep-client-side-fetching",style:{position:"relative"}},e.createElement(a.a,{href:"#when-to-keep-client-side-fetching","aria-label":"when to keep client side fetching 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>'}})),"When to Keep Client-Side Fetching"),"\n",e.createElement(a.p,null,"Not everything should move to the server. In React on Rails, most read-only data is already server-side -- Rails controller props deliver it to your components without any client-side fetching. The table below covers the cases where you should keep client-side fetching instead of relying on Rails controller props or ",e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})),":"),"\n",e.createElement(a.table,null,e.createElement(a.thead,null,e.createElement(a.tr,null,e.createElement(a.th,null,"Use Case"),e.createElement(a.th,null,"Why Client-Side"),e.createElement(a.th,null,"Recommended Tool"))),e.createElement(a.tbody,null,e.createElement(a.tr,null,e.createElement(a.td,null,"Real-time data (WebSocket, SSE)"),e.createElement(a.td,null,"Requires persistent connection"),e.createElement(a.td,null,"Native WebSocket + ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useState</code>'}}))),e.createElement(a.tr,null,e.createElement(a.td,null,"Polling / auto-refresh"),e.createElement(a.td,null,"Periodic updates after initial load"),e.createElement(a.td,null,"React Query / SWR")),e.createElement(a.tr,null,e.createElement(a.td,null,"Optimistic updates"),e.createElement(a.td,null,"Instant UI feedback before server confirms"),e.createElement(a.td,null,"React Query mutations")),e.createElement(a.tr,null,e.createElement(a.td,null,"Infinite scrolling"),e.createElement(a.td,null,"User-driven pagination"),e.createElement(a.td,null,"React Query / SWR")),e.createElement(a.tr,null,e.createElement(a.td,null,"User-triggered searches"),e.createElement(a.td,null,"Response to client interactions"),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">fetch</code>'}})," or React Query")),e.createElement(a.tr,null,e.createElement(a.td,null,"Offline-first features"),e.createElement(a.td,null,"Must work without server"),e.createElement(a.td,null,"Local state + sync")))),"\n",e.createElement(a.h3,{id:"hybrid-pattern-rails-props--client-updates",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#hybrid-pattern-rails-props--client-updates","aria-label":"hybrid pattern rails props  client updates 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>'}})),"Hybrid Pattern: Rails Props + Client Updates"),"\n",e.createElement(a.p,null,"For features that need server-fetched initial data with client-side updates:"),"\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 initial 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">"ChatPage"</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">channelId</span><span class="token operator">:</span> <span class="token variable">@channel</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span>\n               <span class="token symbol">initialMessages</span><span class="token operator">:</span> <span class="token variable">@channel</span><span class="token punctuation">.</span>messages<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">// ChatPage.jsx -- Server Component</span>\n<span class="token keyword">import</span> ChatWindow <span class="token keyword">from</span> <span class="token string">\'./ChatWindow\'</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">ChatPage</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> channelId<span class="token punctuation">,</span> initialMessages <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 class="token class-name">ChannelHeader</span></span> <span class="token attr-name">channelId</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>channelId<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">ChatWindow</span></span> <span class="token attr-name">channelId</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>channelId<span class="token punctuation">}</span></span> <span class="token attr-name">initialMessages</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>initialMessages<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">// ChatWindow.jsx -- Client Component</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> useEffect <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">ChatWindow</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> channelId<span class="token punctuation">,</span> initialMessages <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>messages<span class="token punctuation">,</span> setMessages<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>initialMessages<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 keyword">const</span> ws <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WebSocket</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">wss://api.example.com/chat/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>channelId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    ws<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n      <span class="token function">setMessages</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 operator">...</span>prev<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token punctuation">}</span><span class="token punctuation">;</span>\n    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> ws<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>channelId<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 tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">MessageList</span></span> <span class="token attr-name">messages</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>messages<span class="token punctuation">}</span></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.h2,{id:"loading-states-and-suspense-boundaries",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#loading-states-and-suspense-boundaries","aria-label":"loading states and suspense boundaries 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>'}})),"Loading States and Suspense Boundaries"),"\n",e.createElement(a.h3,{id:"streaming-html-delivery",style:{position:"relative"}},e.createElement(a.a,{href:"#streaming-html-delivery","aria-label":"streaming html delivery 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>'}})),"Streaming HTML Delivery"),"\n",e.createElement(a.p,null,"With synchronous props, Rails loads all data before rendering begins. ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," then streams the rendered HTML as React processes the component tree — the browser receives content as it's rendered rather than waiting for the entire page:"),"\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">"Page"</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> <span class="token variable">@page</span><span class="token punctuation">.</span>title<span class="token punctuation">,</span>\n               <span class="token symbol">main_content</span><span class="token operator">:</span> <span class="token variable">@page</span><span class="token punctuation">.</span>main_content<span class="token punctuation">.</span>as_json<span class="token punctuation">,</span>\n               <span class="token symbol">recommendations</span><span class="token operator">:</span> RecommendationService<span class="token punctuation">.</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token variable">@page</span><span class="token punctuation">)</span><span class="token punctuation">.</span>as_json<span class="token punctuation">,</span>\n               <span class="token symbol">comments</span><span class="token operator">:</span> <span class="token variable">@page</span><span class="token punctuation">.</span>comments<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 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 parameter"><span class="token punctuation">{</span> title<span class="token punctuation">,</span> main_content<span class="token punctuation">,</span> recommendations<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>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 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>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>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">
1SideNav</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\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 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 attr-name">content</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>main_content<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">Recommendations</span></span> <span class="token attr-name">items</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>recommendations<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">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>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>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:"avoiding-popcorn-ui",style:{position:"relative"}},e.createElement(a.a,{href:"#avoiding-popcorn-ui","aria-label":"avoiding popcorn ui 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>'}})),'Avoiding "Popcorn UI"'),"\n",e.createElement(a.p,null,"When many Suspense boundaries resolve at different times, content pops in unpredictably. Group related content in a single boundary:"),"\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: Each section pops in individually</span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Skeleton1</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">}</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">Section1</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">Suspense</span></span><span class="token punctuation">></span></span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Skeleton2</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">}</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">Section2</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">Suspense</span></span><span class="token punctuation">></span></span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Skeleton3</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">}</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">Section3</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">Suspense</span></span><span class="token punctuation">></span></span>\n\n<span class="token comment">
1// Better: Related sections appear together</span>\n<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Suspense</span></span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CombinedSkeleton</span></span> <span class="token punctuation">/></span></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><span class="token class-name">Section1</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">Section2</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">Section3</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">Suspense</span></span><span class="token punctuation">></span></span></code></pre></div>'}}),"\n",e.createElement(a.h3,{id:"dimension-matched-skeletons",style:{position:"relative"}},e.createElement(a.a,{href:"#dimension-matched-skeletons","aria-label":"dimension matched skeletons 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>'}})),"Dimension-Matched Skeletons"),"\n",e.createElement(a.p,null,"Use skeleton components that match the dimensions of the real content to prevent layout shift:"),"\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 keyword">function</span> <span class="token function">StatsSkeleton</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>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stats-panel<span class="token punctuation">"</span></span> <span class="token attr-name">style</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token string">\'200px\'</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>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>skeleton-bar<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 attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>skeleton-bar<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 attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>skeleton-bar<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.h2,{id:"common-mistakes",style:{position:"relative"}}
1,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-sequential-queries-in-the-rails-controller",style:{position:"relative"}},e.createElement(a.a,{href:"#mistake-1-sequential-queries-in-the-rails-controller","aria-label":"mistake 1 sequential queries in the rails controller 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: Sequential queries in the Rails controller"),"\n",e.createElement(a.p,null,"The most common performance regression after migrating to RSC. Since data now comes from the Rails controller (instead of parallel client-side fetches), sequential ActiveRecord queries block the entire page render:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ruby"><pre class="language-ruby"><code class="language-ruby"><span class="token comment"># BAD: 750ms total -- each query waits for the previous one</span>\n<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span>\n  <span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:id</span><span class="token punctuation">]</span><span class="token punctuation">)</span>              <span class="token comment"># 200ms</span>\n  <span class="token variable">@stats</span> <span class="token operator">=</span> Stats<span class="token punctuation">.</span>for_user<span class="token punctuation">(</span><span class="token variable">@user</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span>          <span class="token comment"># 300ms</span>\n  <span class="token variable">@posts</span> <span class="token operator">=</span> Post<span class="token punctuation">.</span>where<span class="token punctuation">(</span><span class="token symbol">user_id</span><span class="token operator">:</span> <span class="token variable">@user</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span>     <span class="token comment"># 250ms</span>\n  stream_view_containing_react_components<span class="token punctuation">(</span><span class="token symbol">template</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"show"</span></span><span class="token punctuation">)</span>\n<span class="token keyword">end</span></code></pre></div>'}}),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Fix:")," Use Ruby threads for independent queries (see ",e.createElement(a.a,{href:"#avoiding-server-side-waterfalls"},"Avoiding Server-Side Waterfalls"),"), use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," for slow props within one component, or split truly independent sections into multiple ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," calls in the ERB view."),"\n",e.createElement(a.h3,{id:"mistake-2-using-server-actions-use-server",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mistake-2-using-server-actions-use-server","aria-label":"mistake 2 using server actions 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 2: Using Server Actions (",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use server'</code>"}}),")"),"\n",e.createElement(a.p,null,"Server Actions are ",e.createElement(a.strong,null,"not supported")," in React on Rails in any environment. The Node renderer is a rendering server -- it has no access to Rails models, sessions, cookies, or CSRF protection."),"\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: Server Actions don\'t have access to Rails</span>\n<span class="token string">\'use server\'</span><span class="token punctuation">;</span>\n<span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">createUser</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n  <span class="token comment">// The Node renderer is a render-only environment -- it has no database</span>\n  <span class="token comment">// connection, no ORM, and no access to Rails models or sessions.</span>\n  <span class="token comment">// This code will fail at runtime.</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: Use a Client Component that submits to a Rails controller endpoint</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<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\n<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">CreateUserForm</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>name<span class="token punctuation">,</span> setName<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">\'\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n\n  <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">handleSubmit</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>\n    e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">
1\'/api/users\'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>\n      <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">\'POST\'</span><span class="token punctuation">,</span>\n      <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>\n        <span class="token string-property property">\'Content-Type\'</span><span class="token operator">:</span> <span class="token string">\'application/json\'</span><span class="token punctuation">,</span>\n        <span class="token string-property property">\'X-CSRF-Token\'</span><span class="token operator">:</span> ReactOnRails<span class="token punctuation">.</span><span class="token function">authenticityToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>\n      <span class="token punctuation">}</span><span class="token punctuation">,</span>\n      <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">user</span><span class="token operator">:</span> <span class="token punctuation">{</span> name <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>\n    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n    <span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">\'\'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>\n  <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>form</span> <span class="token attr-name">onSubmit</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>handleSubmit<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>input</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>name<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">setName</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> <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">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">Create</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>form</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:"mistake-3-forgetting-csrf-tokens-in-fetch-requests",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mistake-3-forgetting-csrf-tokens-in-fetch-requests","aria-label":"mistake 3 forgetting csrf tokens in fetch requests 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: Forgetting CSRF tokens in fetch requests"),"\n",e.createElement(a.p,null,"Rails rejects POST/PUT/PATCH/DELETE requests without a valid CSRF token. This is easy to miss when migrating from forms that included the token automatically:"),"\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: Missing CSRF token -- Rails returns 422 Unprocessable Entity</span>\n<span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">\'/api/items\'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>\n  <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">\'POST\'</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">\'Content-Type\'</span><span class="token operator">:</span> <span class="token string">\'application/json\'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<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>\n\n<span class="token comment">// GOOD: Include the CSRF token</span>\n<span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">\'/api/items\'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>\n  <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">\'POST\'</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>\n    <span class="token string-property property">\'Content-Type\'</span><span class="token operator">:</span> <span class="token string">\'application/json\'</span><span class="token punctuation">,</span>\n    <span class="token string-property property">\'X-CSRF-Token\'</span><span class="token operator">:</span> ReactOnRails<span class="token punctuation">.</span><span class="token function">authenticityToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>\n  <span class="token punctuation">}</span><span class="token punctuation">,</span>\n  <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<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></code></pre></div>'}}),"\n",e.createElement(a.h3,{id:"mistake-4-removing-loading-states-before-adding-streaming",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#mistake-4-removing-loading-states-before-adding-streaming","aria-label":"mistake 4 removing loading states before adding streaming 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: Removing loading states before adding streaming"),"\n",e.createElement(a.p,null,"If you remove ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}})," + loading state but haven't set up streaming with Suspense boundaries, the page appears blank until all server data is ready:"),"\n",e.createElement(a.p,null,e.createElement(a.strong,null,"Fix:")," Complete the streaming setup (",e.createElement(a.a,{href:"rsc-preparing-app.md#step-6-switch-to-streaming-rendering"},"Preparing Your App"),") before converting data-fetching components. Use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," for Rails data that should resolve behind Suspense, and add Suspense boundaries around sections that should stream independently."),"\n",e.createElement(a.h3,{id:"mistake-5-over-serializing-activerecord-objects",style:{position:"relative"}},e.createElement(a.a,{href:"#mistake-5-over-serializing-activerecord-objects","aria-label":"mistake 5 over serializing activerecord objects 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: Over-serializing ActiveRecord objects"),"\n",e.createElement(a.p,null,"Calling ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.as_json</code>'}})," without specifying ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">only:</code>'}})," or ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">include:</code>'}})," can serialize the entire object graph, including associations, timestamps, and internal fields. This bloats the RSC payload and can leak sensitive data:"),"\n",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="ruby"><pre class="language-ruby"><code class="language-ruby"><span class="token comment"># BAD: Serializes everything, including potentially sensitive fields</span>\n<span class="token symbol">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">user</span><span class="token operator">:</span> <span class="token variable">@user</span><span class="token punctuation">.</span>as_json <span class="token punctuation">}</span>\n\n<span class="token comment"># GOOD: Whitelist exactly what the component needs</span>\n<span class="token symbol">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">user</span><span class="token operator">:</span> <span class="token variable">@user</span><span class="token punctuation">.</span>as_json<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">
1:name</span><span class="token punctuation">,</span> <span class="token symbol">:email</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre></div>'}}),"\n",e.createElement(a.h2,{id:"migration-checklist",style:{position:"relative"}},e.createElement(a.a,{href:"#migration-checklist","aria-label":"migration checklist 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>'}})),"Migration Checklist"),"\n",e.createElement(a.h3,{id:"step-1-identify-candidates",style:{position:"relative"}},e.createElement(a.a,{href:"#step-1-identify-candidates","aria-label":"step 1 identify candidates 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>'}})),"Step 1: Identify Candidates"),"\n",e.createElement(a.p,null,"For each component that fetches data:"),"\n",e.createElement(a.ul,null,"\n",e.createElement(a.li,null,"Does it only display data? → Convert to Server Component (pass data as props via ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}}),")"),"\n",e.createElement(a.li,null,"Does it need polling/optimistic updates? → Keep React Query/SWR, add server prefetch"),"\n",e.createElement(a.li,null,"Does it need real-time updates? → Keep client-side, pass initial data from server"),"\n"),"\n",e.createElement(a.h3,{id:"step-2-convert-simple-fetches",style:{position:"relative"}},e.createElement(a.a,{href:"#step-2-convert-simple-fetches","aria-label":"step 2 convert simple fetches 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>'}})),"Step 2: Convert Simple Fetches"),"\n",e.createElement(a.ol,null,"\n",e.createElement(a.li,null,"Remove the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'use client'</code>"}})," directive"),"\n",e.createElement(a.li,null,"Remove ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useState</code>'}})," for data, loading, and error"),"\n",e.createElement(a.li,null,"Remove the ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">useEffect</code>'}})," data fetch"),"\n",e.createElement(a.li,null,"Accept data as props from Rails. For slow data, keep synchronous values in ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">props:</code>'}})," and emit the slow values with ",e.createElement(a.a,{href:"#data-fetching-in-react-on-rails-pro"},e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})),"."),"\n",e.createElement(a.li,null,"Use the matching ERB helper to enable streaming SSR: ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," for synchronous props, or ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," for async props."),"\n",e.createElement(a.li,null,"Remove the API route if it was only used by this component"),"\n"),"\n",e.createElement(a.h3,{id:"step-3-add-suspense-boundaries",style:{position:"relative"}}
1,e.createElement(a.a,{href:"#step-3-add-suspense-boundaries","aria-label":"step 3 add suspense boundaries 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>'}})),"Step 3: Add Suspense Boundaries"),"\n",e.createElement(a.ol,{start:"7"},"\n",e.createElement(a.li,null,"Wrap converted components in ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">&lt;Suspense></code>'}})," at the parent level"),"\n",e.createElement(a.li,null,"Create skeleton components that match content dimensions"),"\n",e.createElement(a.li,null,"Group related data sections in shared boundaries"),"\n"),"\n",e.createElement(a.h3,{id:"step-4-optimize",style:{position:"relative"}},e.createElement(a.a,{href:"#step-4-optimize","aria-label":"step 4 optimize 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>'}})),"Step 4: Optimize"),"\n",e.createElement(a.ol,{start:"10"},"\n",e.createElement(a.li,null,"Use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component</code>'}})," for synchronous props and streaming HTML delivery via React's ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">renderToPipeableStream</code>'}})),"\n",e.createElement(a.li,null,"Use ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">stream_react_component_with_async_props</code>'}})," for slow Rails props behind Suspense boundaries; parallelize independent Ruby queries inside the block when needed to avoid server-side waterfalls"),"\n",e.createElement(a.li,null,"For client-side updates after initial render, use React Query or SWR with ",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">initialData</code>'}}),"/",e.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">fallbackData</code>'}})),"\n"),"\n",e.createElement(a.h2,{id:"next-steps",style:{position:"relative"}},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/cac8473589e96834cd83ae7a914ec0e2/rsc-http-response-patterns.md"},"HTTP Response Ownership")," -- status codes, redirects, and cache headers"),"\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"))}a.A=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)}}}]);
2//# sourceMappingURL=d84bad82-b83c3f5fac295951a043.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.