React on Rails 16.0.0 Release Notes
Also see the Changelog for 16.0.0.
Note: Version 15.0.0 has been retracted. Please upgrade directly from v14 to v16.
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
🚀 Major Performance Breakthrough: Early Hydration
React on Rails now starts hydration even before the full page is loaded! This revolutionary change delivers significant performance improvements across all pages:
- Eliminates Race Conditions: No more waiting for full page load before hydration begins
- Faster Time-to-Interactive: Components hydrate as soon as their server-rendered HTML reaches the client
- Streaming HTML Optimization: Perfect for modern streaming responses - components hydrate in parallel with page streaming
- Async Script Safety: Can use
asyncscripts without fear of race conditions - No More Defer Needed: The previous need for
deferto prevent race conditions has been eliminated
This optimization is particularly impactful for:
- Streamed pages where content loads progressively
- Large pages with many components
- Slow network conditions where every millisecond counts
- Modern web apps requiring fast interactivity
Performance improvement visualization:
The image above demonstrates the dramatic performance improvement:
- Left (Before): Hydration didn't start until the full page load completed, causing a huge delay before hydration
- Right (After): Hydration starts immediately as soon as components are available, without waiting for full page load
- Result: Components now become interactive much faster, eliminating the previous race condition delays
Enhanced Script Loading Strategies
- New configuration option
generated_component_packs_loading_strategyreplacesdefer_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:asyncstrategy)
- Improves page performance by optimizing how component packs are loaded
Breaking Changes
Component Hydration Changes
-
The
defer_generated_component_packsconfiguration has been deprecated. Usegenerated_component_packs_loading_strategyinstead. -
The
generated_component_packs_loading_strategydefaults to:asyncfor Shakapacker ≥ 8.2.0 and:syncfor Shakapacker < 8.2.0. -
The
immediate_hydrationconfiguration now defaults tofalse. Note:immediate_hydrationis a React on Rails Pro (licensed) feature. -
When
generated_component_packs_loading_strategy: :asyncandimmediate_hydration: trueare configured together, they optimize component hydration. Components 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
immediate_hydrationconfiguration (React on Rails Pro licensed feature) makesreact-on-railshydrate components immediately as soon as their server-rendered HTML reaches the client, without waiting for the full page load. - To enable optimized hydration, you can set
immediate_hydration: truein yourconfig/initializers/react_on_rails.rbfile (requires React on Rails Pro license).- You can also enable it for individual components by passing
immediate_hydration: truetoreact_componentorstream_react_component.
- You can also enable it for individual components by passing
- Redux store now supports the
immediate_hydrationoption (React on Rails Pro licensed feature), which defaults toconfig.immediate_hydration(and so tofalseif 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_storehelper withimmediate_hydration: trueorimmediate_hydration: 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:loadlistener 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: :deferinstead - If you were previously using
defer_generated_component_packs: false, usegenerated_component_packs_loading_strategy: :syncinstead - For optimal performance with Shakapacker ≥ 8.2.0, consider using
generated_component_packs_loading_strategy: :async
ESM-only package
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 allowrequirefor ESM modules without any flags. Node v20.17.0+ with--experimental-require-moduleshould 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 setmoduletonodenext.
Finally, if everything else fails, please contact us and we'll help you upgrade or release a dual ESM-CJS version.
globalThis
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.
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
ReduxAppnorComponentWithNoStorewill hydrate untilSimpleStoreis 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.