Skip to content

Commit d5ebf27

Browse files
Recommend async + immediate_hydration even for non-streaming pages
- Update documentation to clarify that defer: true is suboptimal even for non-streaming pages - Recommend async: true with immediate_hydration for best Time to Interactive - Add section about generated_component_packs_loading_strategy config - Update dummy app comment to explain why defer is used (test compatibility) but async is recommended for production Co-authored-by: Abanoub Ghadban <AbanoubGhadban@users.noreply.github.com>
1 parent 4449f53 commit d5ebf27

File tree

2 files changed

+51
-22
lines changed

2 files changed

+51
-22
lines changed

docs/building-features/streaming-server-rendering.md

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -236,36 +236,63 @@ With `defer: true`, your streamed components will:
236236

237237
#### Recommended Approaches
238238

239-
**For Pages WITH Streaming Components:**
239+
**For All Pages (With or Without Streaming Components):**
240240

241241
```erb
242-
<!-- ✅ GOOD: No defer - allows Selective Hydration to work -->
243-
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: false) %>
244-
245-
<!-- ✅ BEST: Use async for even faster hydration (requires Shakapacker ≥ 8.2.0) -->
242+
<!-- ✅ BEST: Use async with immediate_hydration (requires Shakapacker ≥ 8.2.0 and React on Rails Pro) -->
246243
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', async: true) %>
247244
```
248245

249-
**For Pages WITHOUT Streaming Components:**
246+
With `async: true` and the `immediate_hydration` Pro feature enabled, your components hydrate as soon as they're available—before the full page finishes loading. This provides optimal Time to Interactive (TTI) for both streaming and non-streaming pages.
247+
248+
**Alternative for Non-Pro or Pre-8.2.0 Shakapacker:**
249+
250+
```erb
251+
<!-- ✅ GOOD: No defer - allows components to hydrate early -->
252+
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: false) %>
253+
```
254+
255+
**⚠️ Not Recommended (Even for Non-Streaming Pages):**
250256

251257
```erb
252-
<!-- ✅ OK: defer is fine when not using streaming -->
258+
<!-- ⚠️ defer delays hydration until full page load - suboptimal even without streaming -->
253259
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %>
254260
```
255261

256-
#### Why Async is Better Than No Defer
262+
While `defer: true` won't defeat Selective Hydration on non-streaming pages (since there's no streaming to defeat), it still delays all component hydration until after the complete page loads, resulting in slower Time to Interactive.
263+
264+
#### Using generated_component_packs_loading_strategy Config
265+
266+
Instead of manually specifying `async: true` on each `javascript_pack_tag`, you can configure the loading strategy globally for auto-generated component packs:
267+
268+
```ruby
269+
# config/initializers/react_on_rails.rb
270+
ReactOnRails.configure do |config|
271+
# Set loading strategy for auto-generated component packs
272+
# Options: :sync, :async, :defer
273+
config.generated_component_packs_loading_strategy = :async
274+
275+
# Enable immediate hydration (React on Rails Pro feature)
276+
# Components hydrate as soon as their HTML arrives, without waiting for full page load
277+
config.immediate_hydration = true
278+
end
279+
```
280+
281+
This configuration applies to components rendered with auto-generated packs. For manual `javascript_pack_tag` calls, you'll still need to specify `async: true` explicitly.
282+
283+
#### Why Async is Better
257284

258-
With Shakapacker ≥ 8.2.0, using `async: true` provides the best performance:
285+
With Shakapacker ≥ 8.2.0 and React on Rails Pro, using `async: true` + `immediate_hydration: true` provides the best performance:
259286

260-
- **No defer/async**: Scripts block HTML parsing and streaming
261-
- **defer: true**: Scripts wait for complete page load (defeats Selective Hydration)
262-
- **async: true**: Scripts load in parallel and execute ASAP, enabling:
263-
- Selective Hydration to work immediately
264-
- Components to become interactive as they stream in
265-
- Optimal Time to Interactive (TTI)
287+
- **defer: true**: Scripts wait for complete page load, delaying all hydration
288+
- **No defer/async**: Scripts block HTML parsing
289+
- **async: true + immediate_hydration: true**: Scripts load in parallel and components hydrate immediately, enabling:
290+
- Fastest Time to Interactive (TTI)
291+
- Selective Hydration for streaming components
292+
- Early hydration for all components (streaming or not)
266293

267294
#### Migration Timeline
268295

269-
1. **Before Shakapacker 8.2.0**: Use `defer: false` for streaming pages
270-
2. **Shakapacker ≥ 8.2.0**: Migrate to `async: true` for streaming pages
271-
3. **Non-streaming pages**: Can continue using `defer: true` safely (regardless of Shakapacker version)
296+
1. **Before Shakapacker 8.2.0 or without React on Rails Pro**: Use `defer: false`
297+
2. **Shakapacker ≥ 8.2.0 + React on Rails Pro**: Use `async: true` with `immediate_hydration: true` for optimal performance
298+
3. **Avoid `defer: true`**: Even for non-streaming pages, defer delays hydration unnecessarily

spec/dummy/app/views/layouts/application.html.erb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
<%= yield :head %>
1111

12-
<%# defer: true is safe here because this app does NOT use streaming server rendering.
13-
defer also ensures scripts load in the correct order when using auto-generated component packs.
14-
For apps with streaming components, use defer: false or async: true to enable
15-
React 18's Selective Hydration. See docs/building-features/streaming-server-rendering.md %>
12+
<%# Note: Using defer: true here, but async: true with immediate_hydration is recommended for better
13+
Time to Interactive (TTI), even for non-streaming pages. This app keeps defer: true to maintain
14+
test compatibility and demonstrate that defer works when streaming is not used.
15+
For production apps, prefer async: true with immediate_hydration (React on Rails Pro + Shakapacker ≥ 8.2.0),
16+
or use generated_component_packs_loading_strategy: :async in your initializer.
17+
See docs/building-features/streaming-server-rendering.md %>
1618
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %>
1719

1820
<%= csrf_meta_tags %>

0 commit comments

Comments
 (0)