React on Rails 15.0.0 Release Notes
Major Features
🚀 React Server Components Support
Experience the future of React with full RSC integration in your Rails apps:
- Seamlessly use React Server Components
- Reduce client bundle sizes
- Enable powerful new patterns for data fetching
- ⚡️ Requires React on Rails Pro - See the full tutorial
Improved Component Hydration
Major improvements to component and store hydration:
- Components and stores now hydrate immediately rather than waiting for page load
- Enables faster hydration, especially beneficial for streamed pages
- Components can hydrate before the page is fully streamed
- Can use
async
scripts in the page with no fear of race condition - No need to use
defer
anymore
Enhanced Script Loading Strategies
- New configuration option
generated_component_packs_loading_strategy
replacesdefer_generated_component_packs
- Supports three loading strategies:
:async
- Loads scripts asynchronously (default for Shakapacker ≥ 8.2.0):defer
- Defers script execution until after page load (doesn't work well with Streamed HTML as it will wait for the full page load before hydrating the components):sync
- Loads scripts synchronously (default for Shakapacker < 8.2.0) (better to upgrade to Shakapacker 8.2.0 and use:async
strategy)
- Improves page performance by optimizing how component packs are loaded
Breaking Changes
Component Hydration Changes
-
The
defer_generated_component_packs
configuration has been deprecated. Usegenerated_component_packs_loading_strategy
instead. -
The
generated_component_packs_loading_strategy
defaults to:async
for Shakapacker ≥ 8.2.0 and:sync
for Shakapacker < 8.2.0. -
The
force_load
configuration now defaults totrue
. -
The new default values of
generated_component_packs_loading_strategy: :async
andforce_load: true
work together to optimize component hydration. Components now hydrate as soon as their code and server-rendered HTML are available, without waiting for the full page to load. This parallel processing significantly improves time-to-interactive by eliminating the traditional waterfall of waiting for page load before beginning hydration (It's critical for streamed HTML).- The previous need for deferring scripts to prevent race conditions has been eliminated due to improved hydration handling. Making scripts not defer is critical to execute the hydration scripts early before the page is fully loaded.
- The
force_load
configuration makesreact-on-rails
hydrate components immediately as soon as their server-rendered HTML reaches the client, without waiting for the full page load. - If you want to keep the previous behavior, you can set
generated_component_packs_loading_strategy: :defer
orforce_load: false
in yourconfig/initializers/react_on_rails.rb
file.- You can also keep it for individual components by passing
force_load: false
toreact_component
orstream_react_component
.
- You can also keep it for individual components by passing
- Redux store now supports
force_load
option, which defaults toconfig.force_load
(and so totrue
if that isn't set). Iftrue
, the Redux store will hydrate immediately as soon as its server-side data reaches the client.- You can override this behavior for individual Redux stores by calling the
redux_store
helper withforce_load: false
, same asreact_component
.
- You can override this behavior for individual Redux stores by calling the
-
ReactOnRails.reactOnRailsPageLoaded()
is now an async function:-
If you manually call this function to ensure components are hydrated (e.g., with async script loading), you must now await the promise it returns:
// Before ReactOnRails.reactOnRailsPageLoaded(); // Code expecting all components to be hydrated // After await ReactOnRails.reactOnRailsPageLoaded(); // Code expecting all components to be hydrated
-
If you call it in a
turbolinks:load
listener to work around the issue documented in Turbolinks, the listener can be safely removed.
-
Script Loading Strategy Migration
- If you were previously using
defer_generated_component_packs: true
, usegenerated_component_packs_loading_strategy: :defer
instead - If you were previously using
defer_generated_component_packs: false
, usegenerated_component_packs_loading_strategy: :sync
instead - For optimal performance with Shakapacker ≥ 8.2.0, consider using
generated_component_packs_loading_strategy: :async
Store Dependencies for Components
When using Redux stores with multiple components, you need to explicitly declare store dependencies to optimize hydration. Here's how:
The Problem
If you have deferred Redux stores and components like this:
<% redux_store("SimpleStore", props: @app_props_server_render, defer: true) %>
<%= react_component('ReduxApp', {}, {prerender: true}) %>
<%= react_component('ComponentWithNoStore', {}, {prerender: true}) %>
<%= redux_store_hydration_data %>
By default, React on Rails assumes components depend on all previously created stores. This means:
- Neither
ReduxApp
norComponentWithNoStore
will hydrate untilSimpleStore
is hydrated - Since the store is deferred to the end of the page, both components are forced to wait unnecessarily
The Solution
Explicitly declare store dependencies for each component:
<% redux_store("SimpleStore", props: @app_props_server_render, defer: true) %>
<%= react_component('ReduxApp', {}, {
prerender: true
# No need to specify store_dependencies: it automatically depends on SimpleStore
}) %>
<%= react_component('ComponentWithNoStore', {}, {
prerender: true,
# Explicitly declare no store dependencies
store_dependencies: []
}) %>
<%= redux_store_hydration_data %>
This allows ComponentWithNoStore
to hydrate immediately without waiting for SimpleStore
, improving page performance.