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
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
- 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
-
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.
-
- 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
The package is now published as ES Modules instead of CommonJS. In most cases it shouldn't affect your code, as bundlers will be able to handle it. However:
- If you explicitly use
require('react-on-rails')
, and can't change toimport
, upgrade to Node v20.19.0+ or v22.12.0+. They allowrequire
for ESM modules without any flags. Node v20.17.0+ with--experimental-require-module
should work as well. - If you run into
TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'.
TypeScript error, you'll need to upgrade to TypeScript 5.8 and setmodule
tonodenext
.
Finally, if everything else fails, please contact us and we'll help you upgrade or release a dual ESM-CJS version.
globalThis
is now used in code.
It should be available in browsers since 2020 and in Node, but in case your environment doesn't support it, you'll need to shim it using globalthis or core-js.
When using Redux stores with multiple components, you need to explicitly declare store dependencies to optimize hydration. Here's how:
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
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.