react_on_rails_pro 16.2.0.beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.controlplane/Dockerfile +49 -0
- data/.controlplane/controlplane.yml +22 -0
- data/.controlplane/gvc.yml +25 -0
- data/.controlplane/postgres.yml +33 -0
- data/.controlplane/rails.yml +49 -0
- data/.controlplane/redis.yml +18 -0
- data/.gitignore +77 -0
- data/.prettierignore +12 -0
- data/.prettierrc +19 -0
- data/.rspec +2 -0
- data/.rubocop.yml +120 -0
- data/.scss-lint.yml +205 -0
- data/CHANGELOG.md +570 -0
- data/CI_SETUP.md +502 -0
- data/CONTRIBUTING.md +376 -0
- data/Dockerfile +63 -0
- data/Gemfile +8 -0
- data/Gemfile.development_dependencies +74 -0
- data/Gemfile.loader +32 -0
- data/Gemfile.lock +527 -0
- data/LICENSE +98 -0
- data/LICENSE_SETUP.md +272 -0
- data/README.md +577 -0
- data/Rakefile +13 -0
- data/app/controllers/react_on_rails_pro/rsc_payload_controller.rb +7 -0
- data/app/helpers/react_on_rails_pro_helper.rb +360 -0
- data/app/views/react_on_rails_pro/rsc_payload.html.erb +1 -0
- data/babel.config.js +4 -0
- data/docs/bundle-caching.md +205 -0
- data/docs/caching.md +234 -0
- data/docs/code-splitting-loadable-components.md +313 -0
- data/docs/code-splitting.md +349 -0
- data/docs/configuration.md +165 -0
- data/docs/contributors-info/onboarding-customers.md +6 -0
- data/docs/contributors-info/releasing.md +40 -0
- data/docs/contributors-info/style.md +33 -0
- data/docs/home-pro.md +146 -0
- data/docs/installation.md +203 -0
- data/docs/js-memory-leaks.md +22 -0
- data/docs/node-renderer/basics.md +92 -0
- data/docs/node-renderer/debugging.md +38 -0
- data/docs/node-renderer/error-reporting-and-tracing.md +160 -0
- data/docs/node-renderer/heroku.md +102 -0
- data/docs/node-renderer/js-configuration.md +91 -0
- data/docs/node-renderer/troubleshooting.md +5 -0
- data/docs/profiling-server-side-rendering-code.md +179 -0
- data/docs/react-server-components/add-streaming-and-interactivity.md +190 -0
- data/docs/react-server-components/create-without-ssr.md +448 -0
- data/docs/react-server-components/glossary.md +102 -0
- data/docs/react-server-components/how-react-server-components-work.md +243 -0
- data/docs/react-server-components/inside-client-components.md +332 -0
- data/docs/react-server-components/purpose-and-benefits.md +243 -0
- data/docs/react-server-components/rendering-flow.md +86 -0
- data/docs/react-server-components/selective-hydration-in-streamed-components.md +75 -0
- data/docs/react-server-components/server-side-rendering.md +72 -0
- data/docs/react-server-components/tutorial.md +19 -0
- data/docs/release-notes/4.0.md +94 -0
- data/docs/release-notes/v4-react-server-components.md +66 -0
- data/docs/ruby-api.md +11 -0
- data/docs/streaming-server-rendering.md +210 -0
- data/docs/troubleshooting.md +24 -0
- data/docs/updating.md +219 -0
- data/eslint.config.mjs +220 -0
- data/lib/react_on_rails_pro/assets_precompile.rb +230 -0
- data/lib/react_on_rails_pro/cache.rb +88 -0
- data/lib/react_on_rails_pro/concerns/rsc_payload_renderer.rb +38 -0
- data/lib/react_on_rails_pro/concerns/stream.rb +103 -0
- data/lib/react_on_rails_pro/configuration.rb +228 -0
- data/lib/react_on_rails_pro/constants.rb +8 -0
- data/lib/react_on_rails_pro/engine.rb +24 -0
- data/lib/react_on_rails_pro/error.rb +14 -0
- data/lib/react_on_rails_pro/license_public_key.rb +30 -0
- data/lib/react_on_rails_pro/license_validator.rb +188 -0
- data/lib/react_on_rails_pro/prepare_node_renderer_bundles.rb +40 -0
- data/lib/react_on_rails_pro/rendering_error.rb +5 -0
- data/lib/react_on_rails_pro/request.rb +318 -0
- data/lib/react_on_rails_pro/routes.rb +13 -0
- data/lib/react_on_rails_pro/server_rendering_js_code.rb +102 -0
- data/lib/react_on_rails_pro/server_rendering_pool/node_rendering_pool.rb +133 -0
- data/lib/react_on_rails_pro/server_rendering_pool/pro_rendering.rb +117 -0
- data/lib/react_on_rails_pro/stream_cache.rb +61 -0
- data/lib/react_on_rails_pro/stream_request.rb +170 -0
- data/lib/react_on_rails_pro/utils.rb +222 -0
- data/lib/react_on_rails_pro/v8_log_processor.rb +50 -0
- data/lib/react_on_rails_pro/version.rb +6 -0
- data/lib/react_on_rails_pro.rb +23 -0
- data/package-scripts.yml +109 -0
- data/package.json +159 -0
- data/rakelib/dummy_apps.rake +22 -0
- data/rakelib/lint.rake +32 -0
- data/rakelib/public_key_management.rake +155 -0
- data/rakelib/rbs.rake +47 -0
- data/rakelib/run_rspec.rake +81 -0
- data/rakelib/task_helpers.rb +45 -0
- data/rakelib/yard.rake +20 -0
- data/react_on_rails_pro.gemspec +47 -0
- data/readme-gen-docs.md +1 -0
- data/script/bootstrap +33 -0
- data/script/preinstall.js +31 -0
- data/script/setup +23 -0
- data/script/test +38 -0
- data/sig/react_on_rails_pro/cache.rbs +13 -0
- data/sig/react_on_rails_pro/configuration.rbs +100 -0
- data/sig/react_on_rails_pro/error.rbs +4 -0
- data/sig/react_on_rails_pro/utils.rbs +7 -0
- data/sig/react_on_rails_pro.rbs +5 -0
- data/yarn.lock +7599 -0
- metadata +319 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# React Server Components & Streaming in React on Rails Pro
|
|
2
|
+
|
|
3
|
+
## Why RSC with Streaming?
|
|
4
|
+
|
|
5
|
+
### Waterfall Loading Pattern Benefits
|
|
6
|
+
React Server Components with streaming is beneficial for most applications, but it's especially powerful for applications with waterfall loading patterns where data dependencies chain together. For example, when you need to load a user profile before loading their posts, or fetch categories before products. Here's why:
|
|
7
|
+
|
|
8
|
+
### How RSC Fixes Waterfall Server Rendering Issues:
|
|
9
|
+
|
|
10
|
+
When a user visits the page, they'll experience the following sequence:
|
|
11
|
+
|
|
12
|
+
1. The initial HTML shell is sent immediately, including:
|
|
13
|
+
- The page layout
|
|
14
|
+
- Any static content (like the `<h1>` and footer)
|
|
15
|
+
- Placeholder content for the React component (typically a loading state)
|
|
16
|
+
|
|
17
|
+
2. Selective Hydration:
|
|
18
|
+
- Client components hydrate independently as their code chunks load
|
|
19
|
+
- Multiple components can hydrate in parallel
|
|
20
|
+
- User interactions automatically prioritize hydration of interacted components
|
|
21
|
+
- No waiting for full page JavaScript or other components to load
|
|
22
|
+
- Each component becomes interactive immediately after its own hydration
|
|
23
|
+
|
|
24
|
+
### Bundle Size Benefits
|
|
25
|
+
|
|
26
|
+
React Server Components significantly reduce client-side JavaScript by:
|
|
27
|
+
|
|
28
|
+
1. **Server-Only Code Elimination:**
|
|
29
|
+
- Dependencies used only in server components never ship to the client
|
|
30
|
+
- Database queries, API calls, and their libraries stay server-side
|
|
31
|
+
- Heavy data processing utilities remain on the server
|
|
32
|
+
- Server-only NPM packages don't impact client bundle
|
|
33
|
+
|
|
34
|
+
2. **Concrete Examples:**
|
|
35
|
+
- Routing logic can stay server-side
|
|
36
|
+
- Data fetching libraries (like React Query) are often unnecessary
|
|
37
|
+
- Large formatting libraries (e.g., date-fns, numeral) can be server-only
|
|
38
|
+
- Image processing utilities stay on server
|
|
39
|
+
- Markdown parsers run server-side only
|
|
40
|
+
- Heavy validation libraries remain server-side
|
|
41
|
+
|
|
42
|
+
For example, a typical dashboard might see:
|
|
43
|
+
```jsx
|
|
44
|
+
// Before: All code shipped to client
|
|
45
|
+
import { format } from 'date-fns'; // ~30KB
|
|
46
|
+
import { marked } from 'marked'; // ~35KB
|
|
47
|
+
import numeral from 'numeral'; // ~25KB
|
|
48
|
+
|
|
49
|
+
// After: With RSC, these imports stay server-side
|
|
50
|
+
// Client bundle reduced by ~90KB
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### [Selective Hydration](https://github.com/reactwg/react-18/discussions/37) Benefits
|
|
54
|
+
|
|
55
|
+
React's selective hydration is a powerful feature that significantly improves page interactivity by:
|
|
56
|
+
|
|
57
|
+
1. **Independent Component Hydration**
|
|
58
|
+
- Each client component hydrates independently as soon as its code loads
|
|
59
|
+
- No waiting for the entire page's JavaScript to load and execute
|
|
60
|
+
- Components become interactive progressively rather than all at once
|
|
61
|
+
|
|
62
|
+
2. **Interaction-Based Prioritization**
|
|
63
|
+
- React automatically prioritizes hydrating components that users try to interact with
|
|
64
|
+
- If a user clicks a button before hydration, that component gets priority
|
|
65
|
+
- Other components continue hydrating in the background
|
|
66
|
+
- Better perceived performance as users can interact sooner
|
|
67
|
+
|
|
68
|
+
3. **Parallel Processing**
|
|
69
|
+
- Multiple components can hydrate simultaneously
|
|
70
|
+
- Network requests for component code happen in parallel
|
|
71
|
+
- CPU processing for hydration is interleaved efficiently
|
|
72
|
+
- Maximizes browser resources for faster overall interactivity
|
|
73
|
+
|
|
74
|
+
For example, in a typical page layout:
|
|
75
|
+
|
|
76
|
+
```jsx
|
|
77
|
+
<Layout>
|
|
78
|
+
<Suspense fallback={<NavSkeleton />}>
|
|
79
|
+
<Navigation /> {/* Client component */}
|
|
80
|
+
</Suspense>
|
|
81
|
+
<Suspense fallback={<MainSkeleton />}>
|
|
82
|
+
<MainContent /> {/* Client component */}
|
|
83
|
+
<Comments /> {/* Client component */}
|
|
84
|
+
</Suspense>
|
|
85
|
+
<Suspense fallback={<SidebarSkeleton />}>
|
|
86
|
+
<Sidebar /> {/* Client component */}
|
|
87
|
+
</Suspense>
|
|
88
|
+
</Layout>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
With selective hydration:
|
|
92
|
+
- Navigation could become interactive while Comments are still loading
|
|
93
|
+
- If user tries to click a Sidebar button, it gets priority hydration
|
|
94
|
+
- Each component hydrates independently when ready
|
|
95
|
+
- No waiting for all components to load before any become interactive
|
|
96
|
+
|
|
97
|
+
This approach significantly improves the user experience by:
|
|
98
|
+
- Reducing Time to Interactive (TTI) for important components
|
|
99
|
+
- Providing faster response to user interactions
|
|
100
|
+
- Maintaining smooth performance even on slower devices or networks
|
|
101
|
+
- Eliminating the "all or nothing" hydration approach of traditional SSR
|
|
102
|
+
|
|
103
|
+
For a deeper dive into selective hydration, see our [Selective Hydration in Streamed Components](./selective-hydration-in-streamed-components.md) guide.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### Comparison with Other Approaches:
|
|
107
|
+
|
|
108
|
+
1. **Full Server Rendering:**
|
|
109
|
+
- ❌ Delays First Byte until entire page is rendered
|
|
110
|
+
- ❌ All-or-nothing approach to hydration
|
|
111
|
+
- ❌ Must wait for all JavaScript before any interactivity
|
|
112
|
+
- ✅ Good SEO
|
|
113
|
+
- ✅ Complete initial HTML
|
|
114
|
+
|
|
115
|
+
2. **Client-side Lazy Loading:**
|
|
116
|
+
- ❌ Empty initial HTML for lazy components
|
|
117
|
+
- ❌ Must wait for hydration to load
|
|
118
|
+
- ❌ Poor SEO for lazy content
|
|
119
|
+
- ❌ No prioritization of component hydration
|
|
120
|
+
- ❌ Initial page must be loaded and hydrated before loading lazy components
|
|
121
|
+
- ✅ Reduces initial bundle size
|
|
122
|
+
|
|
123
|
+
3. **RSC with Streaming:**
|
|
124
|
+
- ✅ Immediate First Byte
|
|
125
|
+
- ✅ Progressive HTML streaming
|
|
126
|
+
- ✅ SEO-friendly for all content
|
|
127
|
+
- ✅ No hydration waiting for server components
|
|
128
|
+
- ✅ Selective client hydration
|
|
129
|
+
|
|
130
|
+
## Migration Guide
|
|
131
|
+
|
|
132
|
+
### 1. Enable RSC Support
|
|
133
|
+
|
|
134
|
+
Add to your Rails initializer, it makes the magic happen 🪄:
|
|
135
|
+
```ruby
|
|
136
|
+
# config/initializers/react_on_rails_pro.rb
|
|
137
|
+
ReactOnRailsPro.configure do |config|
|
|
138
|
+
config.enable_rsc_support = true
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 2. Update Webpack Configuration
|
|
143
|
+
|
|
144
|
+
Create RSC bundle and make it use the RSC loader:
|
|
145
|
+
```javascript
|
|
146
|
+
// config/webpack/rscWebpackConfig.mjs
|
|
147
|
+
const rscConfig = serverWebpackConfig();
|
|
148
|
+
|
|
149
|
+
// Configure RSC entry point
|
|
150
|
+
rscConfig.entry = {
|
|
151
|
+
'rsc-bundle': rscConfig.entry['server-bundle']
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Add RSC loader
|
|
155
|
+
rules.forEach((rule) => {
|
|
156
|
+
if (Array.isArray(rule.use)) {
|
|
157
|
+
const babelLoader = extractLoader(rule, 'babel-loader');
|
|
158
|
+
if (babelLoader) {
|
|
159
|
+
rule.use.push({
|
|
160
|
+
loader: 'react-on-rails-rsc/WebpackLoader',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 3. Gradual Component Migration
|
|
168
|
+
|
|
169
|
+
#### 1. Mark Entry Points as Client Components
|
|
170
|
+
|
|
171
|
+
Adding the `'use client'` directive to entry points maintains existing functionality while allowing for incremental migration of individual components to server components.
|
|
172
|
+
This approach ensures a smooth transition without disrupting the application's current behavior.
|
|
173
|
+
|
|
174
|
+
```jsx
|
|
175
|
+
// app/components/App.jsx
|
|
176
|
+
'use client';
|
|
177
|
+
|
|
178
|
+
export default function App() {
|
|
179
|
+
// Your existing component code
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### 2. Identify Server Component Candidates:
|
|
184
|
+
- Data fetching components
|
|
185
|
+
- Non-interactive UI
|
|
186
|
+
- Static content sections
|
|
187
|
+
- Layout components
|
|
188
|
+
|
|
189
|
+
#### 3. Progressive Migration Pattern (Top-Down Approach):
|
|
190
|
+
|
|
191
|
+
Start by converting layout and container components at the top of your component tree to server components, moving any interactive logic down to child components. This "top-down" approach maximizes the benefits of RSC.
|
|
192
|
+
|
|
193
|
+
```jsx
|
|
194
|
+
// app/components/Layout.jsx
|
|
195
|
+
// Remove 'use client' - This becomes a server component
|
|
196
|
+
// Move any state/effects to child components first
|
|
197
|
+
export default function Layout({ children }) {
|
|
198
|
+
return (
|
|
199
|
+
<div>
|
|
200
|
+
<Header /> {/* Server component */}
|
|
201
|
+
<Sidebar /> {/* Server component */}
|
|
202
|
+
<main>
|
|
203
|
+
{children} {/* Interactive components like InteractiveWidget remain nested inside */}
|
|
204
|
+
</main>
|
|
205
|
+
<Footer /> {/* Server component */}
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```jsx
|
|
212
|
+
// app/components/InteractiveWidget.jsx
|
|
213
|
+
'use client'; // Keep client directive for interactive components
|
|
214
|
+
|
|
215
|
+
export default function InteractiveWidget() {
|
|
216
|
+
const [state, setState] = useState();
|
|
217
|
+
// Interactive component logic
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### 4. Convert Lazy-Loaded Entry Points:
|
|
222
|
+
|
|
223
|
+
```jsx
|
|
224
|
+
// app/components/LazyLoadedSection.jsx
|
|
225
|
+
// Remove lazy loading wrapper
|
|
226
|
+
// Convert to async server component
|
|
227
|
+
async function LazyLoadedSection() {
|
|
228
|
+
const data = await fetchData();
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
<ServerContent data={data} />
|
|
232
|
+
<ClientInteraction /> {/* Keeps 'use client' */}
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This migration approach allows you to:
|
|
239
|
+
- Maintain existing functionality while migrating
|
|
240
|
+
- Incrementally improve performance
|
|
241
|
+
- Test changes in isolation
|
|
242
|
+
- Keep interactive components working as before
|
|
243
|
+
- Eliminate client-side lazy loading overhead
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# React Server Components Rendering Flow
|
|
2
|
+
|
|
3
|
+
This document explains the rendering flow of React Server Components (RSC) in React on Rails Pro.
|
|
4
|
+
|
|
5
|
+
## Types of Bundles
|
|
6
|
+
|
|
7
|
+
In a React Server Components project, there are three distinct types of bundles:
|
|
8
|
+
|
|
9
|
+
### RSC Bundle (rsc-bundle.js)
|
|
10
|
+
- Contains only server components and references to client components
|
|
11
|
+
- Generated using the RSC Webpack Loader which transforms client components into references
|
|
12
|
+
- Used specifically for generating RSC payloads
|
|
13
|
+
- Configured with `react-server` condition to enable RSC-specific code paths that tell the runtime that this bundle is used for RSC payload generation.
|
|
14
|
+
|
|
15
|
+
### Server Bundle (server-bundle.js)
|
|
16
|
+
- Contains both server and client components in their full form
|
|
17
|
+
- Used for traditional server-side rendering (SSR)
|
|
18
|
+
- Enables HTML generation of any components
|
|
19
|
+
- Does not transform client components into references
|
|
20
|
+
|
|
21
|
+
### Client Bundle
|
|
22
|
+
- Split into multiple chunks based on client components
|
|
23
|
+
- Each file with `'use client'` directive becomes an entry point
|
|
24
|
+
- Code splitting occurs automatically for client components
|
|
25
|
+
- Chunks are loaded on-demand during client component hydration
|
|
26
|
+
|
|
27
|
+
## React Server Component Rendering Flow
|
|
28
|
+
|
|
29
|
+
When a request is made to a page using React Server Components, the following optimized sequence occurs:
|
|
30
|
+
|
|
31
|
+
1. Initial Request Processing:
|
|
32
|
+
- The `stream_react_component` helper is called in the view
|
|
33
|
+
- Makes a request to the node renderer
|
|
34
|
+
- Server bundle's rendering function calls `generateRSCPayload` with the component name and props
|
|
35
|
+
- This executes the component rendering in the RSC bundle
|
|
36
|
+
- RSC bundle generates the payload containing server component data and client component references
|
|
37
|
+
- The payload is returned to the server bundle
|
|
38
|
+
|
|
39
|
+
2. Server-Side Rendering with RSC Payload:
|
|
40
|
+
- The server bundle uses the RSC payload to generate HTML for server components using `RSCServerRoot`
|
|
41
|
+
- `RSCServerRoot` splits the RSC payload stream into two parts:
|
|
42
|
+
- One stream for rendering server components as HTML
|
|
43
|
+
- Another stream for embedding the RSC payload in the response
|
|
44
|
+
- `RSCPayloadContainer` component embeds the RSC payload within the HTML response
|
|
45
|
+
- HTML and embedded RSC payload are streamed together to the client
|
|
46
|
+
|
|
47
|
+
3. Client Hydration:
|
|
48
|
+
- Browser displays HTML immediately
|
|
49
|
+
- React runtime uses the embedded RSC payload for hydration
|
|
50
|
+
- Client components are hydrated progressively without requiring a separate HTTP request
|
|
51
|
+
|
|
52
|
+
This approach offers significant advantages:
|
|
53
|
+
- Eliminates double rendering of server components
|
|
54
|
+
- Reduces HTTP requests by embedding the RSC payload within the initial HTML response
|
|
55
|
+
- Provides faster interactivity through streamlined rendering and hydration
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
sequenceDiagram
|
|
59
|
+
participant Browser
|
|
60
|
+
participant RailsView
|
|
61
|
+
participant NodeRenderer
|
|
62
|
+
participant RSCBundle
|
|
63
|
+
participant ServerBundle
|
|
64
|
+
|
|
65
|
+
Note over Browser,ServerBundle: 1. Initial Request
|
|
66
|
+
Browser->>RailsView: Request page
|
|
67
|
+
RailsView->>NodeRenderer: stream_react_component
|
|
68
|
+
NodeRenderer->>ServerBundle: Execute rendering request
|
|
69
|
+
ServerBundle->>RSCBundle: generateRSCPayload(component, props)
|
|
70
|
+
RSCBundle-->>ServerBundle: RSC payload with:<br/>- Server components<br/>- Client component refs
|
|
71
|
+
ServerBundle-->>NodeRenderer: Generate HTML using RSC payload
|
|
72
|
+
|
|
73
|
+
Note over Browser,ServerBundle: 2. Single Response
|
|
74
|
+
NodeRenderer-->>Browser: Stream HTML with embedded RSC payload
|
|
75
|
+
|
|
76
|
+
Note over Browser: 3. Client Hydration
|
|
77
|
+
Browser->>Browser: Process embedded RSC payload
|
|
78
|
+
loop For each client component
|
|
79
|
+
Browser->>Browser: Fetch component chunk
|
|
80
|
+
Browser->>Browser: Hydrate component
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Next Steps
|
|
85
|
+
|
|
86
|
+
To learn more about how to render React Server Components inside client components, see [React Server Components Inside Client Components](./inside-client-components.md).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Selective Hydration in React Server Components
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
React has introduced a powerful enhancement to server-side rendering through streaming and React Server Components - selective hydration. This feature fundamentally changes how pages become interactive in the browser.
|
|
6
|
+
|
|
7
|
+
Previously, with traditional server-side rendering, the browser had to wait for the entire page to load and all JavaScript to execute before any part could become interactive. This created a noticeable delay in page interactivity, especially for larger applications.
|
|
8
|
+
|
|
9
|
+
With selective hydration, React can now hydrate different parts of the page independently and asynchronously. Key benefits include:
|
|
10
|
+
|
|
11
|
+
- Components can become interactive as soon as their code and data are available, without waiting for the entire page
|
|
12
|
+
- React automatically prioritizes hydrating components that users are trying to interact with
|
|
13
|
+
|
|
14
|
+
This approach significantly improves both perceived and actual performance by making the most relevant parts interactive first.
|
|
15
|
+
|
|
16
|
+
## Try Selective Hydration with React Server Component Page
|
|
17
|
+
|
|
18
|
+
Let's try selective hydration with the React Server Component Page we created in the [SSR React Server Components](./server-side-rendering.md).
|
|
19
|
+
|
|
20
|
+
Let's add a component that is very slow to load into the page.
|
|
21
|
+
|
|
22
|
+
```jsx
|
|
23
|
+
const LongWaitingComponent = async () => {
|
|
24
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
25
|
+
return <div>Long waiting component</div>;
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Add the component to the page.
|
|
30
|
+
|
|
31
|
+
```jsx
|
|
32
|
+
// app/javascript/packs/components/ReactServerComponentPage.jsx
|
|
33
|
+
const ReactServerComponentPage = () => {
|
|
34
|
+
return (
|
|
35
|
+
<div>
|
|
36
|
+
<ReactServerComponent />
|
|
37
|
+
<Suspense fallback={<div>Loading The Long Waiting Component...</div>}>
|
|
38
|
+
<LongWaitingComponent />
|
|
39
|
+
</Suspense>
|
|
40
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
41
|
+
<Posts />
|
|
42
|
+
</Suspense>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Fixing Compatibility Issue that Blocks Hydration
|
|
49
|
+
|
|
50
|
+
When you run the page, you should see "Loading The Long Waiting Component..." in the browser for 5 seconds. Then, the component is rendered and the page becomes interactive.
|
|
51
|
+
|
|
52
|
+
You can notice that the page doesn't become interactive until the Long Waiting Component is rendered, which contradicts what we discussed about selective hydration.
|
|
53
|
+
|
|
54
|
+
This happens because React on Rails by default adds the scripts that hydrate components as `defer` scripts, which only execute after the whole page is loaded. Since the page is being streamed, this means the scripts won't run until all components have been server-side rendered and streamed to the browser.
|
|
55
|
+
|
|
56
|
+
This default behavior was kept for backward compatibility, as there were previously race conditions that could occur when using `async` scripts before the page fully loaded. However, these race conditions have been fixed in the latest React on Rails release.
|
|
57
|
+
|
|
58
|
+
To enable true selective hydration, we need to configure React on Rails to load scripts as `async` scripts by setting `generated_component_packs_loading_strategy: :async` in the initializer:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
# config/initializers/react_on_rails.rb
|
|
62
|
+
ReactOnRails.configure do |config|
|
|
63
|
+
config.generated_component_packs_loading_strategy = :async
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Now, when you run the page, you can see that while the Long Waiting Component is loading ⏳, the other components are interactive ✨🖱️
|
|
68
|
+
|
|
69
|
+
## Conclusion
|
|
70
|
+
|
|
71
|
+
Selective hydration is a powerful feature that allows React to become interactive as soon as its code and data are available, without waiting for the entire page to load. This approach significantly improves both perceived and actual performance by making the most relevant parts interactive first.
|
|
72
|
+
|
|
73
|
+
## Next Steps
|
|
74
|
+
|
|
75
|
+
Now that you understand how to use selective hydration in React Server Components, you can proceed to the next article: [How React Server Components Work](how-react-server-components-work.md) to learn about the technical details and underlying mechanisms of React Server Components.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# SSR React Server Components
|
|
2
|
+
|
|
3
|
+
Before reading this document, please read:
|
|
4
|
+
1. [Create React Server Component without SSR](./create-without-ssr.md)
|
|
5
|
+
2. [Add Streaming and Interactivity to RSC Page](./add-streaming-and-interactivity.md)
|
|
6
|
+
|
|
7
|
+
These documents provide essential background on React Server Components and how they work without Server Side Rendering (SSR).
|
|
8
|
+
|
|
9
|
+
## Update the React Server Component Page
|
|
10
|
+
|
|
11
|
+
Let's make React on Rails server-side render the React Server Component Page we created in the previous articles.
|
|
12
|
+
|
|
13
|
+
Update the `react_server_component_without_ssr.html.erb` view to pass `prerender: true` to the `react_component` helper.
|
|
14
|
+
|
|
15
|
+
```erb
|
|
16
|
+
<%= react_component("ReactServerComponentPage",
|
|
17
|
+
prerender: true,
|
|
18
|
+
trace: true,
|
|
19
|
+
id: "ReactServerComponentPage-react-component-0") %>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Now, when you visit the page, you should see part of the React Server Component page rendered in the browser. Then, we get the error:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
The server did not finish this Suspense boundary: The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This error occurs because the `react_component` helper uses React's `renderToString` function, which renders the React page synchronously in a single pass. This approach isn't suitable for React Server Components, which can contain asynchronous operations and need progressive streaming of content.
|
|
29
|
+
|
|
30
|
+
Instead, we need to use the streaming capabilities provided by React on Rails Pro, as detailed in the [streaming server rendering documentation](../streaming-server-rendering.md). These helpers internally use React's `renderToPipeableStream` API, which supports:
|
|
31
|
+
|
|
32
|
+
1. Server-side rendering of async components
|
|
33
|
+
2. Progressive streaming of HTML chunks to the client as components finish rendering
|
|
34
|
+
3. Incremental hydration, where each component can be hydrated independently as it loads, rather than waiting for the entire application
|
|
35
|
+
|
|
36
|
+
To enable streaming SSR for React Server Components, we need to:
|
|
37
|
+
|
|
38
|
+
1. Create a new view called `react_server_component_ssr.html.erb` with the following content:
|
|
39
|
+
|
|
40
|
+
```erb
|
|
41
|
+
# app/views/pages/react_server_component_ssr.html.erb
|
|
42
|
+
<%= stream_react_component("ReactServerComponentPage",
|
|
43
|
+
id: "ReactServerComponentPage-react-component-0") %>
|
|
44
|
+
|
|
45
|
+
<h1>React Server Component with SSR</h1>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
2. Ensure our controller includes `ReactOnRailsPro::Stream` and use the `stream_view_containing_react_components` helper to render the view:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# app/controllers/pages_controller.rb
|
|
52
|
+
class PagesController < ApplicationController
|
|
53
|
+
include ReactOnRailsPro::Stream
|
|
54
|
+
|
|
55
|
+
def react_server_component_ssr
|
|
56
|
+
stream_view_containing_react_components(template: "pages/react_server_component_ssr")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. Add the route to `config/routes.rb`:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
# config/routes.rb
|
|
65
|
+
get "/react_server_component_ssr", to: "pages#react_server_component_ssr"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Now, when you visit the page, you should see the entire React Server Component page rendered in the browser. And if you viewed the page source, you should see the HTML being streamed to the browser.
|
|
69
|
+
|
|
70
|
+
## Next Steps
|
|
71
|
+
|
|
72
|
+
Now that you understand how to enable server-side rendering (SSR) for your React Server Components, you can proceed to the next article: [Selective Hydration in Streamed Components](selective-hydration-in-streamed-components.md) to learn about React's selective hydration feature and how it improves page interactivity.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# React Server Components Tutorial
|
|
2
|
+
|
|
3
|
+
This tutorial will guide you through learning [React Server Components (RSC)](https://react.dev/reference/rsc/server-components) with React on Rails Pro, from basic concepts to advanced features. The tutorial is divided into several parts that build upon each other:
|
|
4
|
+
|
|
5
|
+
1. [Create React Server Component without SSR](create-without-ssr.md) - Learn the fundamentals of React Server Components by creating a basic RSC page without server-side rendering.
|
|
6
|
+
|
|
7
|
+
2. [Add Streaming and Interactivity to RSC Page](add-streaming-and-interactivity.md) - Enhance your RSC page with streaming capabilities and client-side interactivity using Suspense and client components.
|
|
8
|
+
|
|
9
|
+
3. [Server-Side Rendering for React Server Components](server-side-rendering.md) - Add SSR to your React Server Components for improved initial page load performance.
|
|
10
|
+
|
|
11
|
+
4. [Selective Hydration in Streamed Components](selective-hydration-in-streamed-components.md) - Learn about React's selective hydration feature and how it improves page interactivity.
|
|
12
|
+
|
|
13
|
+
5. [How React Server Components Work](how-react-server-components-work.md) - Dive deep into the technical details and underlying mechanisms of React Server Components.
|
|
14
|
+
|
|
15
|
+
6. [React Server Components Rendering Flow](rendering-flow.md) - Understand the detailed rendering flow of RSC, including bundle types, current limitations, and future improvements.
|
|
16
|
+
|
|
17
|
+
7. [React Server Components Inside Client Components](inside-client-components.md) - Learn how to render server components inside client components.
|
|
18
|
+
|
|
19
|
+
Each part of the tutorial builds on the concepts from previous sections, so it's recommended to follow them in order. Let's begin with creating your first React Server Component!
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# 4.0 Release Notes
|
|
2
|
+
|
|
3
|
+
## 🚀 Major New Features
|
|
4
|
+
|
|
5
|
+
### React Server Components (RSC) - Full Production Support
|
|
6
|
+
React on Rails Pro now provides comprehensive support for React Server Components, enabling you to build the next generation of React applications:
|
|
7
|
+
|
|
8
|
+
- **Full RSC Integration**: Seamlessly use React Server Components in your Rails apps with zero configuration
|
|
9
|
+
- **Bundle Optimization**: Automatic client/server code splitting that significantly reduces client-side JavaScript
|
|
10
|
+
- **Server-Side Data Fetching**: Direct access to databases, APIs, and server resources from React components
|
|
11
|
+
- **Progressive Hydration**: Client components hydrate independently for optimal performance
|
|
12
|
+
- **RSC Payload Streaming**: Efficient streaming of component data with embedded payloads
|
|
13
|
+
- **Compatible with React Router**: [Use React Router with RSC](../react-server-components/inside-client-components.md)
|
|
14
|
+
|
|
15
|
+
See our [complete RSC tutorial](../react-server-components-tutorial.md) to get started.
|
|
16
|
+
|
|
17
|
+
### Advanced Streaming Server Rendering
|
|
18
|
+
Building on React 19's streaming capabilities, React on Rails Pro delivers:
|
|
19
|
+
|
|
20
|
+
- **Progressive HTML Streaming**: Send page content as it becomes available
|
|
21
|
+
- **Suspense Boundary Support**: Handle async components with proper loading states
|
|
22
|
+
- **Selective Hydration**: Components become interactive as soon as they're ready
|
|
23
|
+
- **Error Boundary Handling**: Graceful error handling during streaming with configurable error raising
|
|
24
|
+
- **Async Console Log Replay**: Debug async server-side rendering with client-side console output
|
|
25
|
+
|
|
26
|
+
### Enhanced Error Reporting & Tracing
|
|
27
|
+
Completely redesigned error reporting system with:
|
|
28
|
+
|
|
29
|
+
- **Custom Integration Support**: Integrate with any error reporting service (Sentry, Honeybadger, or custom)
|
|
30
|
+
- **Sentry SDK v8 Support**: Latest Sentry integration with improved performance
|
|
31
|
+
- **Flexible Configuration**: Configure error reporting according to your preferences
|
|
32
|
+
- **Enhanced Tracing**: Better visibility into rendering performance and issues
|
|
33
|
+
|
|
34
|
+
## Performance Improvements
|
|
35
|
+
|
|
36
|
+
### Node Renderer Architecture
|
|
37
|
+
- **Fastify 5 Integration**: Upgraded from Express to Fastify for significantly better performance
|
|
38
|
+
- **HTTP/2 Cleartext Communication**: Rails communicates with Node renderer over HTTP/2 instead of HTTP/1.1
|
|
39
|
+
- **HTTPX Client**: Replaced Net::HTTP with HTTPX for improved connection handling
|
|
40
|
+
- **Pino Logging**: Switched from Winston to Pino for better performance and Fastify compatibility
|
|
41
|
+
|
|
42
|
+
These changes provide:
|
|
43
|
+
- Better performance when Node renderer is deployed on the same machine as Rails
|
|
44
|
+
- Significantly improved performance when deployed in separate workloads
|
|
45
|
+
- Enhanced connection reuse and multiplexing capabilities
|
|
46
|
+
- Better error handling and process management
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Changes Specific For RSC Rendering Optimization
|
|
50
|
+
- **Cross-Bundle Communication**: Components can now interact seamlessly across different bundles using the new `runOnOtherBundle` function, enabling advanced composition and modularization patterns.
|
|
51
|
+
- **Single-Pass Server Component Rendering**: Server components are rendered just once within the RSC bundle, then efficiently reused for both SSR and client hydration—eliminating redundant work and improving performance.
|
|
52
|
+
- **Reduced HTTP Requests**: RSC payloads are now embedded directly into the initial HTML response. No need to make an additional request to fetch the RSC payload.
|
|
53
|
+
- **Protocol v2.0 – Unified Bundle Management**: The new protocol allows simultaneous upload of both server and RSC bundles in a single request, supporting multiple bundle uploads and providing robust, flexible bundle management for complex applications.
|
|
54
|
+
|
|
55
|
+
## Breaking Changes
|
|
56
|
+
|
|
57
|
+
### Configuration Updates
|
|
58
|
+
- **Sentry/Honeybadger**: Remove old configuration options starting with `sentry` or `honeybadger`
|
|
59
|
+
- **Timer Polyfills**: `includeTimerPolyfills` is renamed to `stubTimers`
|
|
60
|
+
- **Environment Variables**: `RENDERER_STUB_TIMERS` instead of `INCLUDE_TIMER_POLYFILLS`
|
|
61
|
+
- **Error Reporting**: Follow the [Error Reporting and Tracing](../node-renderer/error-reporting-and-tracing.md) documentation for new setup
|
|
62
|
+
|
|
63
|
+
### Dependency Requirements
|
|
64
|
+
- **Ruby 3+**: Dropped support for Ruby 2.7 (EOL)
|
|
65
|
+
- **React on Rails 15+**: Required for RSC and streaming features
|
|
66
|
+
- **Node 20+**: Strongly recommended (older versions require specific package.json resolutions)
|
|
67
|
+
|
|
68
|
+
### Package.json Resolutions (for Node < 20)
|
|
69
|
+
If using older Node versions, add to your `package.json`:
|
|
70
|
+
```json
|
|
71
|
+
"resolutions": {
|
|
72
|
+
"@fastify/formbody": "^7.4.0",
|
|
73
|
+
"@fastify/multipart": "^8.3.1",
|
|
74
|
+
"fastify": "^4.29.0"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Getting Started
|
|
79
|
+
|
|
80
|
+
- **RSC Tutorial**: [Complete React Server Components Guide](../react-server-components-tutorial.md)
|
|
81
|
+
- **Streaming SSR**: [Streaming Server Rendering Documentation](../streaming-server-rendering.md)
|
|
82
|
+
- **Error Reporting**: [Error Reporting and Tracing Setup](../node-renderer/error-reporting-and-tracing.md)
|
|
83
|
+
- **Performance**: [Caching and Optimization Guide](../caching.md)
|
|
84
|
+
|
|
85
|
+
## Support & Community
|
|
86
|
+
|
|
87
|
+
- **Documentation**: Comprehensive guides and tutorials available
|
|
88
|
+
- **Examples**: Working examples in the spec/dummy application
|
|
89
|
+
- **GitHub**: Active development and community support
|
|
90
|
+
- **Discussions**: Join the [React on Rails community](https://forum.shakacode.com/) for help and updates
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
*React on Rails Pro 4.0 represents a major evolution in server-side React rendering, bringing React Server Components and advanced streaming to the Rails ecosystem with enterprise-grade performance and reliability.*
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# React on Rails Pro: Introducing React Server Components & SSR Streaming
|
|
2
|
+
|
|
3
|
+
**Subject: 🚀 Revolutionary Performance Boost: React Server Components & SSR Streaming Now Available in React on Rails Pro**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Dear Valued React on Rails Pro Client,
|
|
8
|
+
|
|
9
|
+
We're thrilled to announce a major update: React on Rails Pro now supports **React Server Components** and **Server‑Side Rendering (SSR) Streaming**. These features have driven significant performance gains in real‑world applications—here’s how they can transform yours.
|
|
10
|
+
|
|
11
|
+
## 🎯 What This Means for Your Applications
|
|
12
|
+
|
|
13
|
+
* **Faster load times**
|
|
14
|
+
* **Smaller JavaScript bundles**
|
|
15
|
+
* **Better Core Web Vitals**
|
|
16
|
+
* **Improved SEO**
|
|
17
|
+
* **Smoother user interactions**
|
|
18
|
+
|
|
19
|
+
## 🔥 React Server Components
|
|
20
|
+
|
|
21
|
+
Server Components execute on the server and stream HTML to the client—no extra JavaScript in your bundle. Real‑world results include:
|
|
22
|
+
|
|
23
|
+
* **62% reduction** in client‑side bundle size on productonboarding.com when migrating to RSC [[1]]
|
|
24
|
+
* **63% improvement** in Google Speed Index on the RSC version of the same site [[1]]
|
|
25
|
+
* **52% smaller** JavaScript codebase and Lighthouse scores rising from \~50 to 90+ on GeekyAnts.com [[2]]
|
|
26
|
+
|
|
27
|
+
## 🌊 SSR Streaming
|
|
28
|
+
|
|
29
|
+
SSR Streaming sends HTML to the browser in chunks as it’s generated, enabling progressive rendering:
|
|
30
|
+
|
|
31
|
+
* **30% faster** full‑page load times at Hulu by combining streaming SSR with Server Components [[3]]
|
|
32
|
+
* Popular libraries like styled‑components v3.1.0 have introduced streaming SSR support as the next generation of React app rendering [[4]]
|
|
33
|
+
|
|
34
|
+
## 📊 Core Web Vitals & TTI Improvements
|
|
35
|
+
|
|
36
|
+
* **60% faster** Time to Interactive on Meta’s developer portal after adopting RSC (from 3.5 s to \~1.4 s) [[5]]
|
|
37
|
+
* **45% quicker** First Contentful Paint in the same migration [[5]]
|
|
38
|
+
* **50% lower** server response time with Server Components [[5]]
|
|
39
|
+
* **15% improvement** in Core Web Vitals and **23% reduction** in Time to First Byte at Airbnb after RSC migration [[5]]
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
Adopting these features in React on Rails Pro will help you deliver faster, leaner, and more SEO‑friendly applications with fewer client‑side resources.
|
|
44
|
+
|
|
45
|
+
**Ready to get started?**
|
|
46
|
+
|
|
47
|
+
1. Update to the latest React on Rails Pro version
|
|
48
|
+
2. Follow our [RSC & SSR Streaming migration guide](https://github.com/shakacode/react_on_rails_pro/blob/abanoubghadban/pro465/use-rsc-payload-to-render-server-components-on-server/docs/react-server-components-tutorial.md)
|
|
49
|
+
|
|
50
|
+
Let’s make your apps faster—together.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 📚 References
|
|
55
|
+
|
|
56
|
+
1. productonboarding.com experiment: 62% bundle reduction, 63% Speed Index gain ([frigade.com][1])
|
|
57
|
+
2. GeekyAnts.com case study: 52% code reduction, Lighthouse 50→90+ ([geekyants.com][2])
|
|
58
|
+
3. Hulu—30% faster full‑page loads with streaming SSR + RSC ([questlab.pro][3])
|
|
59
|
+
4. styled‑components v3.1.0: introduced streaming SSR support as the next generation of React rendering. ([medium.com][4])
|
|
60
|
+
5. QuestLab: Meta’s RSC migration—30% JS reduction, 60% faster TTI, 45% faster FCP, 50% lower server response ([questlab.pro][5])
|
|
61
|
+
|
|
62
|
+
[1]: https://frigade.com/blog/bundle-size-reduction-with-rsc-and-frigade
|
|
63
|
+
[2]: https://geekyants.com/en-gb/blog/boosting-performance-with-nextjs-and-react-server-components-a-geekyantscom-case-study
|
|
64
|
+
[3]: https://www.compilenrun.com/docs/framework/nextjs/nextjs-ecosystem/nextjs-case-studies/#case-study-3-hulus-streaming-platform
|
|
65
|
+
[4]: https://medium.com/styled-components/v3-1-0-such-perf-wow-many-streams-c45c434dbd03
|
|
66
|
+
[5]: https://questlab.pro/blog-posts/web-development/wd-pl-2024-articleId912i1h212818
|
data/docs/ruby-api.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Ruby API
|
|
2
|
+
|
|
3
|
+
Note: you will need access to our **private** React on Rails Pro repository to open the following links.
|
|
4
|
+
|
|
5
|
+
## View Helpers
|
|
6
|
+
|
|
7
|
+
See the [app/helpers/react_on_rails_pro_helper.rb](https://github.com/shakacode/react_on_rails_pro/blob/master/app/helpers/react_on_rails_pro_helper.rb) source.
|
|
8
|
+
|
|
9
|
+
## Utility Methods
|
|
10
|
+
|
|
11
|
+
See the [lib/react_on_rails_pro/utils.rb](https://github.com/shakacode/react_on_rails_pro/blob/master/lib/react_on_rails_pro/utils.rb) source.
|