Breaking Changes
- [Pro] Removed the undocumented
ReactOnRailsPro::Cache.fetch_react_componentclass API:
Pro apps should use the supported cached helper APIs (cached_react_component,
cached_react_component_hash, and related helpers) instead of calling the low-level cache class
directly. The helper cache path still preserves generated-pack loading on cache hits, tag
registration, andexpires_athandling internally. Fixes
#4497.
#4541 by
justin808. - [Pro] React Server Components now require the stable React 19.2.x RSC line: React on Rails Pro 17 requires
react-on-rails-rsc >= 19.2.1 < 20, React >= 19.2.7, and matching React DOM. The Pro generator scaffolds that coordinated stable runtime and rejects prerelease RSC packages below the stable peer floor. The adoptedreact-on-rails-rscartifact uses the React on Rails Pro commercial terms rather than MIT and reportsSEE LICENSE IN LICENSE.mdin npm metadata. SetREACT_ON_RAILS_PRO_DISABLE_VERSION_CHECK=1only as an emergency rollout escape hatch to downgrade startup errors to warnings. #4357. #4490 and #4670 by justin808. - Removed the inert
config.server_render_methodoption: The open-source configuration no longer acceptsconfig.server_render_method. The option never selected a server render method — the open-source gem always renders with ExecJS — and its validator raisedReactOnRails::Errorat boot for any value other than blank or"ExecJS". Setting it now raisesNoMethodErrorat boot, so delete anyconfig.server_render_method = ...line fromconfig/initializers/react_on_rails.rb;rake react_on_rails:doctoralso flags the stale line. For a standalone Node rendering process, use React on Rails Pro's Node renderer, configured viaReactOnRailsPro.configure. Fixes #4415. #4423 by justin808. - Removed three deprecated configuration options (
config.generated_assets_dirs,config.skip_display_none,config.defer_generated_component_packs): These were deprecated in v16 and are gone in v17. Setting any of them now raisesNoMethodErrorat boot; delete the stale lines fromconfig/initializers/react_on_rails.rb(rake react_on_rails:doctorflags them). Migration: deleteconfig.generated_assets_dirs— public asset paths come frompublic_output_pathinconfig/shakapacker.yml; deleteconfig.skip_display_none— it had no runtime effect; replaceconfig.defer_generated_component_packs = truewithconfig.generated_component_packs_loading_strategy = :defer, and simply deleteconfig.defer_generated_component_packs = false(the removed option was truthy-gated — only= trueset:defer;= falsewas a no-op that fell through to the default strategy, so it did not mean:sync; set:syncexplicitly only if you relied on synchronous loading). The default strategy is:asyncfor Pro or:deferfor non-Pro on Shakapacker 8.2.0+, and:syncon older Shakapacker. Fixes #4419. #4432 by justin808. - Removed the never-wired
RenderRequest/JsCodeBuilder/RenderingStrategyrendering layer: The internal strategy-pattern classesReactOnRails::RenderRequest,ReactOnRails::JsCodeBuilder,ReactOnRails::RenderingStrategy(withExecJsStrategy), and — in Pro —ReactOnRailsPro::JsCodeBuilderandReactOnRailsPro::RenderingStrategy::NodeStrategy, plus the undocumentedReactOnRails.rendering_strategyandReactOnRails.js_code_buildermodule accessors, are removed. This scaffolding was built for the strategy-pattern refactor in #2905 (closed without wiring it in) and was never invoked on any production server-rendering path — SSR runs throughServerRenderingJsCodeandServerRenderingPool, which never touched this layer. These constants and accessors were internal and undocumented; if you reference them in application code, remove the reference (the layer performed no work). Fixes #4414. #4437 by justin808. - Ruby 3.3+ is required for React on Rails v17: The open-source gem now requires Ruby
>= 3.3.0, aligning it with React on Rails Pro,create-react-on-rails-app, and the CI minimum matrix. React on Rails v16 remains the upgrade path for applications that must stay on Ruby 3.2 or older. #3500 by justin808. - [Pro] Node Renderer now requires Ruby 3.3+ for the async-http transport: The
react-on-rails-progem now requires Ruby>= 3.3(raised from>= 3.0) becauseasync-httpdepends on Ruby 3.3 features. Upgrade Ruby before moving to this release. Seedocs/pro/updating.mdfor the full upgrade guide. #3320 by AbanoubGhadban. - [Pro]
config.renderer_http_pool_sizenow limits async-http connections per renderer client: Existing numeric values now cap concurrent async-http connections for each renderer client instead of sizing a persistent process-wide connection pool. HTTP/2 may multiplex request streams over those pooled connections. Settingnilkeeps the default connection limit and does not make the async-http client unlimited. Persistent connection reuse is automatic when a long-livedFiber.scheduleris present. Seedocs/pro/updating.mdfor the full upgrade guide. #3320 by AbanoubGhadban.
Added
-
[Pro] React 18 support for non-RSC streaming SSR:
stream_react_componentwith synchronous
props is now explicitly supported on React 18 as well as React 19. Permanent packed-artifact
coverage verifies a production Webpack build and progressive Suspense output on React 18 without
installing or bundlingreact-on-rails-rsc; async props and React Server Components remain React
19-only. Fixes #4642.
#4658 by
justin808. -
Generated Rails response TypeScript contracts: Rails apps can now register explicit JSON response
contracts withReactOnRails::TypeScriptResponseTypesand run
rake react_on_rails:generate_response_typesto emit importable.d.tsdeclarations plus a
RailsResponseTypeslookup map for TanStack Query clients. Fixes
#4247. #4259 by justin808. -
Typed Rails action callers for TanStack Query mutations: The
react-on-rails/railsAction
subpath now exportscreateRailsAction, a same-origin JSON caller that attaches Rails CSRF headers and
lets mutation code type responses with the generatedRailsResponseType<'controller.action'>lookup.
Fixes #4248. #4260 by justin808. -
[Pro] Typed Rails action callers for TanStack Query mutations: The Pro package mirrors the
createRailsActionhelper atreact-on-rails-pro/railsAction. #4260 by justin808. -
[Pro] Loader-time RSC prefetch through the provider cache: Client-router loaders can now
callprefetchServerComponentto warm a bounded page-global prefetch store thatRSCProvider
adopts on the nextRSCRouterender. Prefetches no-op when the SSR payload is already embedded,
support loader abort signals, and resolve without unhandled rejections after fetch/decode failure
self-eviction. Fixes #4460.
#4489 by
justin808. -
bin/dev cleanclears generated bundles and caches: The command stops development processes, readsconfig/shakapacker.ymlorSHAKAPACKER_CONFIG, removes configured Shakapacker public/private output and cache paths plus common Rails, JavaScript, and renderer bundle caches, and skips unsafe paths outside the app root. #4218 by justin808. -
[Pro] Buffered RSC rendering for static pages:
buffered_stream_react_componentandcached_buffered_stream_react_component
now render server components through the Pro streaming/RSC renderer while
returning complete HTML to Rails, so static or cacheable pages can avoid
ActionController::Liveresponse commits when progressive streaming is not
needed. When RSC support is enabled, prerendered Pro fragment cache keys now
include the RSC bundle digest, or a missing-bundle sentinel until that bundle
exists, so deploys that update only the RSC bundle invalidate cached RSC output
consistently. Fixes
#4263.
#4268 by
justin808. -
[Pro] Cached static RSC public-page helper and diagnostics:
cached_static_rsc_componentcaches stripped static RSC HTML for public pages
that intentionally skip the generated page pack, while respecting
auto_load_bundle: falseand preserving explicit sidecar assets. Static RSC
render diagnostics report cache hit/miss state, redacted cache-key digests,
HTML and stripped payload bytes, emitted asset bytes, and RSC client-reference
entries for performance investigations. Fixes
#4295 and
#4296.
#4386 by
justin808. -
[Pro] Opt-in browser performance marks for streamed RSC observability: Pro streaming can now emit inline browser marks for RSC stream completion, embedded Flight payload chunks, Node-side flushes, hydration start, and first interactive client effects, with byte counts and timing details that avoid serialized props or payload contents. The documented path uses body-delivered marks and a fallback queue instead of HTTP trailers, so apps can measure streamed RSC responses across CDN paths that may strip or hide trailer timing. Fixes #4205, #4206, and #4207. #4222 by justin808.
-
[Pro]
Server-Timingattribution for streamed RSC responses: Whenrsc_stream_observability: true, the streamed RSC response now also carries aServer-Timingresponse header with aror_stream_shellmetric (Rails shell render, including the blocking wait for each component's first renderer chunk), set in the narrow window beforeActionController::Livecommits headers and appended to any existingServer-Timingentries. The Node renderer additionally emits aror_renderer_preparemetric (execution-context build plus render start) on its HTTP response. This is the server/renderer-side complement to the browser performance marks above, letting a reviewer attribute the streamedresponseEndtail to a specific phase rather than guessing. Total/stream-complete time stays on thereact-on-rails:rsc:streammark becauseActionController::Livedoes not support HTTP trailers. Closes #4239. #4251 by justin808. -
[Pro] Focused RSC doctor artifact diagnostics:
rake react_on_rails:doctor:rscand
Doctor.new(only: ...)now run the RSC artifact checks directly, reporting missing,
stale, invalid, or dev-server-backed RSC bundle and manifest files with rebuild guidance
while explaining when Pro path resolution is unavailable. Closes
#4204.
#4223 by
justin808. -
[Pro] Bidirectional async props streaming (pull mode):
stream_react_component_with_async_propscan now let React request lazy props during incremental rendering, complementing the existing eager push model. The stream protocol carriespropRequest/renderCompletecontrol messages from the node renderer back to Rails,AsyncPropsManagercan request or reject props on demand, and the Pro dummy app now covers pure pull, mixed push/pull, Redis-backed fixtures, and rejection/error-boundary scenarios. Closes #4046. #4048. -
Owner Stacks in development error reports: When a React component throws during rendering, React on Rails now enriches its development error reporting with React 19.1+'s dev-only
captureOwnerStackoutput — the chain of components that rendered the failing one (e.g.at Avatar/at PostCard/at PostList). On the server-side rendering path, the owner stack is captured synchronously inside the Pro streamingonErrorcallback and appended to the error's stack, so it flows through to the Rails-sideReactOnRails::PrerenderError/SmartError(:server_rendering_error) output and the streamed shell-error HTML. On the client, recoverable hydration mismatches (onRecoverableError) automatically gain an owner-stack line in the branded development log, and apps that register their ownonCaughtError/onUncaughtErrorhandler get an additive[ReactOnRails] Render error ... Owner stack:line for client render errors. To avoid displacing React's own built-in development diagnostics (component stacks, error-boundary hints), React on Rails does not auto-attach caught/uncaught handlers solely to log owner stacks. Owner-stack capture requires React >= 19.1 running its development build, so it is a strict no-op on older React and in all production builds (no capture, nocaptureOwnerStackcall), asserted by tests. The ExecJS rendering path is out of scope (capture must happen JS-side, synchronously, inside React's error callback). Closes #3887. #4089 by justin808. -
hydrate_on scheduling:
react_componentnow accepts ahydrate_on:option to defer client hydration of an island until it is needed —:immediate(default, unchanged),:visible(hydrate when the container scrolls near the viewport viaIntersectionObserver), or:idle(hydrate during browser idle time viarequestIdleCallback). Deferred roots are cleaned up on Turbo/Turbolinks navigation and re-scheduled if their node is detached and reattached; unsupported modes raise, and non-:immediatemodes are rejected when React on Rails Pro is installed. Closes #3890. #4037 by justin808. -
[Pro] Tag-based cache revalidation (a Next.js
revalidateTaganalog): The fragment-caching helpers (cached_react_component,cached_react_component_hash,cached_stream_react_component,cached_async_react_component) now accept an optionalcache_tags:option (String, Proc, any object responding tocache_keysuch as an ActiveRecord model, or an Array of any mix), and the newReactOnRailsPro.revalidate_tag(tag)/revalidate_tags(*tags)API deletes every cached entry registered under a tag via aRails.cache-backed tag->key index. A newReactOnRailsPro::Cache::RevalidatesActiveRecord concern (revalidates_react_cache) drives revalidation fromafter_commit, so the model that owns the data also owns cache invalidation (and composes withtouch:). Revalidation is best-effort with correctness bounded byexpires_in(a development-mode warning fires whencache_tags:is used without it); index growth is bounded by the newconfig.cache_tag_index_expires_in(default 7 days) andconfig.cache_tag_index_max_keys(default 5,000) settings. Existingcache_key:-only behavior is unchanged. Closes #3871. #3964 by justin808. -
React 19 root error callbacks:
ReactOnRails.setOptions({ rootErrorHandlers: { onRecoverableError, onCaughtError, onUncaughtError } })registers React's root error callbacks globally; React on Rails applies them to everyhydrateRoot/createRootcall it makes and invokes them with an extra context argument whosecomponentNameanddomNodeIdfields are optional. In development, recoverable hydration errors now log an actionable React on Rails message (component name, dom id, component stack, and a link to the new Debugging Hydration Mismatches guide) alongside React's default error reporting, which stays intact so window-'error'-based tooling keeps working. PartialrootErrorHandlersupdates merge per key, so registering one callback later does not drop the others. On React <19 (and <18 foronRecoverableError), React on Rails retains registrations for future upgrades, but the current runtime cannot invoke unsupported callbacks and logs a one-time console warning. On React on Rails Pro RSC/streaming hydration paths, user callbacks chain with (never replace) Pro's internal recoverable-error handler. Addresses #3892. #3933 by justin808. -
useRailsFormhook +render_model_errorscontroller concern (an InertiauseForm-style bridge to Rails controllers): New React hookuseRailsForm(importable fromreact-on-rails/useRailsForm) makes posting a React form to a plain Rails controller turnkey:data/setData, per-fielderrors,processing,wasSuccessful, submit verbs (post/put/patch/delete/submit),reset/clearErrors/setError, automatic CSRF attachment from the Rails csrf-token meta tag, JSON request/response handling, and mapping of422+{ errors: { field: ["message"] } }responses onto per-field error state. Success results surface aredirectTotarget (followed-redirect URL or JSONredirect_tohint) without navigating, forward-compatible with the client-routing work in #3873. The gem side adds the opt-inReactOnRails::Controller::FormRespondersconcern whoserender_model_errors(record)renders ActiveModel errors in exactly that shape, so validations stay in the model with no API layer and no client-side duplication. Includes a new Forms and Mutations docs page (with an InertiauseFormmapping table and a Server Functions #3867 cross-link) and a runnable dummy-app example (/rails_form). v1 is fetch-only;transform,recentlySuccessful, and file-uploadprogressare deferred. Closes #3872. #3942 by justin808. -
[Pro] Built-in node renderer
/healthand/readyprobe endpoints: The node renderer can now register first-class liveness (GET /health->200with a status-only body) and readiness (GET /ready->503until the answering worker is online and has at least one server bundle compiled, then200) endpoints, replacing the hand-rolledconfigureFastifyhealth-check recipe for the common case. The endpoints are off by default and enabled with the newenableHealthEndpointsconfig option (orRENDERER_ENABLE_HEALTH_ENDPOINTS=true,TRUE,yes,YES, or1); they are unauthenticated like/infobut expose no runtime version or path details. The1alias is scoped toRENDERER_ENABLE_HEALTH_ENDPOINTSso existing node-renderer boolean environment flags keep their previous parsing behavior. Includes a new Health and Readiness Endpoints docs page with working Kubernetes (tcpSocket+execwithcurl --http2-prior-knowledge-- the h2c listener cannot be probed with HTTP/1.1httpGet), ECS, and Docker Compose probe examples. Closes #3880. #3939 by justin808. -
[Pro] Source-mapped stack traces in the Node renderer: SSR errors now point at the original TypeScript/JavaScript
file:line:columninstead of bundled positions. When the server bundle carries an inline source map (or a.mapfile is available next to the uploaded bundle), the renderer captures the map text for the VM's bundle generation and lazily parses it on the first error before remapping stack frames — both for exceptions returned to Rails as renderer errors and for stacks captured inside the bundle that surface throughReactOnRails::PrerenderError. Bundles are also now evaluated with their real file path, so even unmapped stacks name the bundle file rather thanevalmachine.<anonymous>. No per-request overhead: map parsing and frame remapping happen only when an error's stack is accessed, and parsed maps are cached per bundle generation. Uses Node's built-inmodule.SourceMap(no new dependencies). Part of #3893. #3940 by justin808. -
Machine-readable doctor output (
FORMAT=json):bin/rails react_on_rails:doctor FORMAT=json(andReactOnRails::Doctor.new(format: :json)) now emits a stable, versioned JSON report —schema_version,ror_version, overallstatus, per-check entries with stable snake_case ids (pass/warn/failstatus, most-severemessage, fulldetails), and asummaryof check counts — so coding agents and tooling can consume diagnosis results without parsing the human-formatted text. Stray check output is routed to stderr so stdout stays valid JSON; the default human-readable output and exit-code semantics are unchanged. Split out from the MCP-server RFC in #3870. Closes #3943. #3948 by justin808. -
Consumer-facing AI-agent guidance scaffolded into generated and installed apps:
rails generate react_on_rails:install(and thereforecreate-react-on-rails-app, which delegates to it) now writes a concise, app-scopedAGENTS.mdplus thin editor-pointer files (CLAUDE.md,.cursor/rules/react-on-rails.mdc,.github/copilot-instructions.md) so an AI coding agent dropped into a fresh app already knows how to register a component, use thereact_componentview helper, choose.client/.serverbundles, recover from the top runtime errors (sourced fromSmartError), and runbin/rails react_on_rails:doctor. The errors section tracks the liveSmartErrormessages, and the editor files are pointers (not copies). Emission is gated by--agent-files/--no-agent-files(default on) in both paths and never overwrites an existing file. Cross-links the eval-harness work in #3832. Closes #3868. #3924 by justin808. -
First-party font optimization helper (a
next/font/localanalog): NewReactOnRails::FontHelper#react_on_rails_font_faceview helper returns<head>markup for a committed, self-hosted.woff2font: a<link rel="preload" as="font" type="font/woff2" crossorigin>, an@font-facewithfont-display: swap, and an optional metric-matched fallback@font-face(size-adjustplusascent-override/descent-override/line-gap-override) so the system fallback occupies the same space as the web font and the swap produces no layout shift (CLS). Self-hosting through the asset pipeline avoids any third-party font-host request. Includes a new Font Optimization docs page (self-hosting, preload,font-display, thesize-adjustfallback technique with a worked Inter-over-Arial derivation, subsetting guidance, and a CLS note) and a runnable dummy-app example. v1 covers thenext/font/localcommitted-file path and the non-streamingreact_component_hashhead-injection path. Closes #3875 (partial). Deferred sub-tasks tracked in #3921. #3923 by justin808. -
Stable SmartError codes and generated error reference: SmartError messages now include stable
ROR###codes and canonical documentation URLs, anddocs/oss/reference/error-reference.mdis generated from the SmartError definitions with a drift check. Fixes #3894. #3936 by justin808. -
Preload link helper for generated component packs: Added
react_on_rails_preload_linksso layouts can emit preload, modulepreload, and stylesheet preload tags for auto-bundled component packs from the Shakapacker manifest. Fixes #3889. #3935 by justin808. -
Tailwind CSS v4 generator option:
rails generate react_on_rails:install --tailwindandcreate-react-on-rails-app --tailwindnow install Tailwind CSS v4, wire@tailwindcss/postcssfor Webpack or Rspack, and style the generated server-rendered HelloWorld example with extracted component CSS support. Interactivecreate-react-on-rails-appruns recommend Tailwind by default while still allowing users to opt out. Fixes #3895. #3937 by justin808. -
[Pro] RSC registration entry path override: Generated RSC precompile hooks, discovery builds, and client-reference stale checks now honor
REACT_ON_RAILS_RSC_REGISTRATION_ENTRY_PATHso apps that writeserver-component-registration-entry.jsoutside the default generated path can point React on Rails Pro at the exact entry while retaining the existing fallback scan and stale-manifest cleanup. Fixes #3621. #3712 by justin808. -
[Pro] RSC
unstable_cachewith Redis L2 and tiered caching: Addedunstable_cachefor RSC rendering with deterministic cache-key serialization (React flight protocol encoding with sorted object keys), aRedisCacheHandlerfor cross-worker L2 caching, aTieredCacheHandlerfor L1/L2 composition with configurable L1 TTL caps, and per-worker single-flight render coalescing. Resolves #3702. #3705. -
[Pro] RSC manifest client reference discovery during precompile: Generated RSC Webpack configs now run
RSCReferenceDiscoveryPluginthrough the Shakapacker precompile hook to emitrsc-client-references.json, use that manifest for RSC client-reference bundling, and warn when the selected manifest is stale. #3556 by ihabadham. -
[Pro] Imperative refetch API for
<RSCRoute>:<RSCRoute>now accepts an optionalreftyped asRSCRouteHandle, exposingrefetch()so a parent or sibling can refetch a server component without knowing itscomponentNameorcomponentProps. A newuseCurrentRSCRoute()hook returns the same handle for client components rendered inside the RSC subtree (for example, an inline "Refresh" button rendered by the server component itself); calling it outside an<RSCRoute>ancestor throwsuseCurrentRSCRoute must be used inside an <RSCRoute>. Both APIs auto-update the rendered tree with no caller-sidesetKey/useStateworkaround and propagate to every<RSCRoute>instance bound to the same cache key. The internal cache invalidation runs inside a React transition, so old content stays visible while the new RSC payload streams in without a Suspense-fallback flash. The existinguseRSC().refetchComponent(name, props)API and theServerComponentFetchError-based retry flow are unchanged. Fixes #3106. #3552 by AbanoubGhadban. -
[Pro]
<RSCRoute ssr={false}>defers initial RSC payload generation:<RSCRoute>now acceptsssr={false}to skip server-side RSC payload generation for that route — the server streams the nearest<Suspense>fallback and the client fetches the payload through the existingRSCProviderpath (cache lookup,/rsc_payload/:componentNamefetch,ServerComponentFetchError, anduseRSC().refetchComponent(...)retry).ssrdefaults totrue, so existing routes are unchanged and a mixed page can server-render some routes while deferring others. Deferred roots that do not manually callwrapServerComponentRendererare now supported automatically: RSC-enabled generated client packs register a default RSC provider (also exported asreact-on-rails-pro/registerDefaultRSCProvider/clientfor manual entrypoints) that wraps auto-bundledreact_component(..., prerender: false)and deferred-onlystream_react_componentroots. Completes #3101. #3318, #3394 by ihabadham. -
Ruby 4.0 CI support: Updated OSS latest-runtime CI coverage, local CI switching guidance, and public compatibility docs to test Ruby 4.0 while keeping Ruby 3.3 as the minimum supported CI lane. #3529 by justin808.
-
[Pro] HTTP rolling-deploy endpoint auto-mount: Configuring
config.rolling_deploy_adapter = ReactOnRailsPro::RollingDeployAdapters::Httpnow automatically mountsReactOnRailsPro::RollingDeploy::BundlesControlleratconfig.rolling_deploy_mount_path(default/react_on_rails_pro/rolling_deploy). Set the mount path tonilor blank to opt out and keep a manualdraw_routesmount; apps that previously mounted the default route manually should remove that route or give secondary manual mounts a distinctas_prefix:to avoid duplicate named-route errors. Fixes #3476. #3504 by justin808. -
[Pro]
unstable_cachefor React Server Component fragment caching: New experimentalunstable_cache(fn, options)wrapper memoizes a server component's serialized RSC payload — replaying the stored bytes on a cache hit and tee-ing output to both the response and the cache store on a miss. Ships with aCacheHandlerinterface and a default in-memory LRU handler (register custom backends viaregisterCacheHandler), plus tag-based invalidation throughunstable_revalidateTag(tag)that broadcasts across all Node Renderer workers via a newPOST /cache/revalidate-tagendpoint and a Ruby-sideReactOnRailsPro::RSCCache.revalidate_tag(tag). Closes #3324. #3325 by AbanoubGhadban. -
[Pro] Node Renderer integration API now exposes lifecycle hooks:
react-on-rails-pro-node-renderer/integrations/apinow exports the tracing reset, provider-state, Fastify lifecycle, and worker shutdown hooks needed by integrations such as OpenTelemetry, keeping integrations inside the supported public boundary. Fixes #3419. #3456 by justin808. -
[Pro] Built-in HTTP rolling-deploy adapter (scaffold): New
ReactOnRailsPro::RollingDeployAdapters::Httpadapter pairs with a mountableReactOnRailsPro::RollingDeploy::BundlesControllerso the currently-deployed Rails server can directly serve previously-deployed bundles to the next deploy's build CI — no S3 bucket, IAM, or extra gem required. The controller exposes authenticatedGET /manifestandGET /bundles/:hashendpoints using bearer-token auth (constant-time compare, 32-byte minimum), and the adapter pulls bundle tarballs (stdlib-only gzip/tar compose-extract with path-traversal proofing, regular-files-only guards, and a 200 MB zip-bomb cap). Configure viaconfig.rolling_deploy_adapter = ReactOnRailsPro::RollingDeployAdapters::Http,config.rolling_deploy_token, andconfig.rolling_deploy_previous_url. Seedocs/pro/rolling-deploy-adapters.mdfor setup. This is part 1 of a multi-PR series — a hard HTTPS gate, streaming download, and additional hardening land in follow-ups. #3379 by justin808. -
[Pro] OpenTelemetry integration for the Node Renderer: New optional integration at
react-on-rails-pro-node-renderer/integrations/opentelemetrythat adds distributed tracing via standard OpenTelemetry. Users enable it by installing the@opentelemetry/*and@fastify/otelpackages (optional peer deps) and callinginit({ fastify: true, tracing: true })from their renderer entrypoint, beforereactOnRailsProNodeRenderer(). Provides auto-instrumented HTTP and Fastify spans, an SSR root span (ror.ssr.request), and render-path sub-spans (ror.bundle.build_execution_context,ror.bundle.upload,ror.vm.execute,ror.result.prepare,ror.incremental.stream,ror.incremental.process_chunk). Configuration follows standard OpenTelemetry env-var conventions (OTEL_EXPORTER_OTLP_ENDPOINT,OTEL_SERVICE_NAME,OTEL_RESOURCE_ATTRIBUTES, etc.); defaults toBatchSpanProcessorin production andSimpleSpanProcessorotherwise. The integration is fully optional — users who do not enable it pay zero runtime cost, and the renderer has no direct dependency on OpenTelemetry. Closes #2156. #3382 by justin808. -
[Pro] Richer Node Renderer span attributes:
ror.bundle.uploadnow recordsbytes.total(sum of bundle + asset upload source sizes);ror.vm.executerecordsbundle.timestamp;ror.result.preparerecordsresponse.bytes(UTF-8 byte length of the rendered response, omitted for streamed responses). Only byte counts and identifiers are recorded — request payloads and rendered HTML are never written into span attributes. ThesubSpanAPI now passes aSubSpanControllerto the wrapped function so integrations can attach attributes computed during the work; existing implementations must callfn(controller)(a no-op controller is fine when no span is created). Closes #3390. #3422 by justin808. -
react-on-rails/webpackHelperssubpath export withreactDomClientWarning: New webpack helper export so React 16/17 consumers can suppress the harmlessModule not found: Can't resolve 'react-dom/client'warning with a one-liner instead of remembering a regex. The require insidereactApisis guarded by a runtime React-version check, so this warning never reflects a real failure, but webpack still emits it at build time because the staticrequire('react-dom/client')cannot be tree-shaken without breaking React 18+. PassreactDomClientWarningtoignoreWarnings(Webpack 5 / Shakapacker) orstats.warningsFilter(Webpack 4 / Webpacker 5). Fixes #3137. #3358 by justin808. -
bin/devdeterministic port allocation viaREACT_ON_RAILS_BASE_PORT(andCONDUCTOR_PORT):bin/devnow derives Rails / webpack-dev-server / node-renderer ports from a single base port whenREACT_ON_RAILS_BASE_PORT(orCONDUCTOR_PORT, for Conductor.build workspaces) is set: Rails =base + 0, webpack =base + 1, renderer =base + 2. This makes parallel worktrees and coding-agent sandboxes collision-free without per-service env vars. The priority chain is base port → explicit per-service env vars (PORT,SHAKAPACKER_DEV_SERVER_PORT) → auto-detection. Behavior note: when base-port mode is active, any pre-setPORT,SHAKAPACKER_DEV_SERVER_PORT,RENDERER_PORT, or non-matchingREACT_RENDERER_URL(and the legacyRENDERER_URL, when already set) is unconditionally overwritten with the derived value (a warning is printed before each override). This applies in allbin/devmodes includingbin/dev prod, whereSHAKAPACKER_DEV_SERVER_PORTis also derived/overwritten for tooling consistency even though the production-like mode does not run webpack-dev-server. Sub-process env preservation: to keep these derived values consistent across spawned processes,bin/devnow also preservesRENDERER_PORT,REACT_RENDERER_URL, andSHAKAPACKER_SKIP_PRECOMPILE_HOOKacross Bundler's env reset (previously onlyPORTandSHAKAPACKER_DEV_SERVER_PORTwere preserved); this prevents nestedshakapackercommands from silently re-running the precompile hook or losing the renderer URL. #3142 by justin808. -
[Pro]
bin/devauto-derivesREACT_RENDERER_URLfromRENDERER_PORT: When onlyRENDERER_PORTis set,bin/devnow setsREACT_RENDERER_URL=http://localhost:RENDERER_PORTso Rails reaches the right port by default. Users running a remote or non-localhost node renderer (Docker service, remote host) should setREACT_RENDERER_URLexplicitly so it is not replaced with the localhost default. #3142 by justin808. -
[Pro] Pre-seed renderer cache for Docker builds: New
react_on_rails_pro:pre_seed_renderer_cacherake task copies compiled server bundles into the Node Renderer's bundle-hash cache directory structure during Docker image builds, eliminating the 410→retry cold-start latency (200ms–1s+) on the first SSR request after deployment. SupportsRENDERER_SERVER_BUNDLE_CACHE_PATH, RSC bundles, and rolling-deploy guidance centered on current and previous bundle hashes. The legacypre_stage_bundle_for_node_renderertask now stages the same cache layout via symlinks for same-filesystem workflows. Note:RENDERER_BUNDLE_PATHis now deprecated in favor ofRENDERER_SERVER_BUNDLE_CACHE_PATHacross both tasks. Existing users withRENDERER_BUNDLE_PATHset will see a deprecation warning on stderr. #3124 by justin808. -
[Pro] Rolling-deploy adapter protocol: New
config.rolling_deploy_adapterpluggable module (protocol:previous_bundle_hashes,fetch,upload) that seeds previously-deployed bundle hashes into the Node Renderer cache, preventing 410→retry for draining-version requests during rolling deploys.assets:precompileauto-callsuploadin production-like environments so the next deploy can fetch the just-built bundle.PREVIOUS_BUNDLE_HASHESenv var overrides discovery for CI.react_on_rails:doctorprobes the adapter and reports protocol conformance, discovery latency, and resolved cache dir. Each seeded hash carries its ownloadable-stats.json/ RSC manifests so client-side hydration stays consistent with the deployed asset pipeline for that hash. Seedocs/pro/rolling-deploy-adapters.mdfor the full protocol spec and reference implementations (S3, Control Plane, Filesystem). #3173 by justin808. -
[Pro] Async props with incremental React Server Component rendering: Added the
stream_react_component_with_async_propsandrsc_payload_react_component_with_async_propsview helpers, which accept a block to declare props that are fetched concurrently and streamed to the rendering component as each value becomes available. The React component renders its shell immediately (with<Suspense>fallbacks) and progressively re-renders as async prop promises resolve, dramatically improving Time to First Byte for pages with slow data fetches. Components access async props through thegetReactOnRailsAsyncPropfunction injected into props (typed via the newWithAsyncPropsTypeScript helper). Requiresconfig.enable_rsc_support = true. This is an additive feature — existingstream_react_componentandrsc_payload_react_componentcalls are unaffected. #2903 by AbanoubGhadban.
Changed
-
[Pro] Rolling deploys can seed bundles from multiple endpoints: The built-in HTTP adapter's
config.rolling_deploy_previous_urlssetting accepts a URL string, comma-separated string, or Array,
unions bundle hashes advertised by every endpoint, and fetches from each endpoint in order. This lets
promoted images seed from both the build environment and the promotion target instead of missing the
target's draining bundle. The matching build argument isROLLING_DEPLOY_PREVIOUS_URLS. See
Promotion deploys need a boot seed.
#4544 by
justin808. -
[Pro] Published artifacts now identify the commercial license accurately: The Pro gem reports
LicenseRef-LICENSE, while thereact-on-rails-proandreact-on-rails-pro-node-renderernpm packages
reportSEE LICENSE IN LICENSE.md. All three packed artifacts include React on Rails Pro EULA v2.3 instead
of presenting the software asUNLICENSEDor shipping stale terms. EULA v2.3 clarifies organization-owned
internal and public use, preserves free educational/demo use, and defines one product-level attribution per
covered HTML document. All plan types and permitted free uses now require that attribution; a valid public
attribution reportsLicensedwithout exposing the Organization, while private server diagnostics retain it.
#4660 by
justin808. -
[Pro] Reduced tag-index cache work for streamed cache writes: Multi-tag cache registration now
batch-reads existing tag indexes when supported, and streamed cache misses defer cache writes and tag
registration until queued response chunks have drained. Streamed and async cache misses also register tags
with the same completion-time cache options used for the cache write, keeping TTL snapshots aligned. Fixes
#4319.
#4443 by
justin808. -
Lighter
npx create-react-on-rails-appinstall: The scaffolder now colorizes its terminal output with the zero-dependencypicocolorsinstead ofchalk@4, droppingchalkand its transitive tree (ansi-styles,supports-color,color-convert,color-name,has-flag) from the package's runtime dependencies. This trims the weight of every coldnpx create-react-on-rails-appinstall. The TTY color-fallback now also mirrors chalk@4/supports-color's actual signal precedence (recognized CI vendors,TERM,COLORTERM, Windows) instead of coloring any non-dumb TTY, matching the prior colored-output behavior in full. Fixes #4411 and #4455. #4444 and #4473 by justin808. -
[Pro] Removed unused
addressable/rainbowruntime gem dependencies:react_on_rails_pro.gemspecno longer declares direct runtime dependencies onaddressableorrainbow— no Pro Ruby code referenced either gem, and both still arrive transitively through the requiredreact_on_railsgem, so behavior is unchanged. Fixes #4416. #4422 by justin808. -
[Pro] Fail fast for RSC with Rspack v1: When React Server Components are enabled and Shakapacker is
configured for Rspack, app boot andreact_on_rails:doctornow reject@rspack/corev1 or a missing
@rspack/corepackage with explicit Rspack v2 upgrade instructions. This guard only runs when RSC is enabled,
so Rspack v1 remains allowed for non-RSC apps, and bundler detection now honorsSHAKAPACKER_ASSETS_BUNDLER
beforeconfig/shakapacker.yml.
#4289 by justin808. -
Redux is now hidden from the V17 install generator path: The
react_on_rails:install --reduxoption is no longer shown in install generator help or usage text, and recovery guidance no longer recommends--reduxfor new installs. The hidden legacy path and directreact_on_rails:react_with_reduxgenerator now warn that Redux scaffolding is legacy while keeping runtime Redux APIs available. Closes #4272 and #4273. #4277 by justin808. -
create-react-on-rails-appnow defaults to Pro for React 19.2 support: Running
npx create-react-on-rails-app my-appno longer asks setup questions and generates the recommended
React on Rails Pro scaffold by default. Automation note: non-TTY environments, including CI and piped
commands, previously auto-selected Standard mode; they now select Pro. Add--standardto any command
that intentionally checks or creates an open-source-only scaffold. Use--rscwhen you want Pro with the
generated React Server Components example. The canonicalreactonrails.com"Start a new app" AI-agent
prompt (prompts.yml) was updated to match, so AI assistants following it also default to the Pro path
and mention the license requirement. #4217 and
#4232 by justin808. -
[Pro] Missing renderer password error now leads with the local-development fix: When the Pro renderer password is unset and both
RAILS_ENVandNODE_ENVare unset, the fail-closed error from both the Ruby configuration guard and the Node renderer now includes explicitexport RAILS_ENV=development NODE_ENV=developmentguidance. Password-optional behavior for explicit development/test envs and password-required behavior for production-like or mixed envs are unchanged. Fixes #4201. #4211 by justin808. -
Breaking (types only):
RenderFunctionno longer accepts the legacy 3-argument renderer shape: The exportedRenderFunctiontype is now exactly the 2-argument server/client render-function form ((props, railsContext) => RenderFunctionResult), equal to the existingServerRenderFunction. It previously also accepted a 3-argument(props, railsContext, domNodeId) => RenderFunctionResultarm, which let nonsensical role combinations typecheck (a server render-function "returning" a renderer teardown, or a renderer "returning" a server-render hash). Renderer functions — the 3-argument form that owns its own DOM mount and may return a{ teardown }wrapper — should now be typedRendererFunction.ReactComponentOrRenderFunctionalready includesRendererFunction, so renderer-shaped functions remain registerable. The tighter type also drops the re-narrowingascasts the unified type forced increateReactOutputand the Pro tanstack-router render function. This is a compile-time-only change with no runtime behavior difference; only TypeScript consumers that annotated a 3-argument renderer asRenderFunctionneed to switch it toRendererFunction. Closes #3592. #4096 by justin808. -
[Pro] RSC peer-compatibility warn-tier floor raised to stable
19.0.5: The Pro node renderer'srecommendedMinforreact-on-rails-rscis now the published stable19.0.5(previously the dormant19.0.2). Anyone still on an older 19.x build (19.0.2–19.0.4) now gets a loud startup warning that they are missing the coordinated RSC fixes shipped in19.0.5(FOUC stylesheet preloading, async manifest signatures);19.0.5+ no longer warns. Refs #3632. #4078 by justin808. -
[Pro] RSC peer wildcard replaced with an explicit range: The Pro npm package's optional
react-on-rails-rscpeer dependency is now^19.0.5(>= 19.0.5 < 20.0.0) instead of"*", so installs are floored at the RSC CSS FOUC fix release19.0.5and capped below the next major20.0.0, rather than"*"accepting any version including pre-FOUC builds and an unknown future major. Within the19.xline, per-version compatibility is enforced by the Pro node renderer's runtime version check (rscPeerSupport.ts), not by this advisory peer range. Fixes #3965. #4082 by justin808. -
[Pro] RSC peer compatibility accepts the coordinated React 19.2 floor: The Pro node renderer now allows React and React DOM
19.2.xstarting at19.2.7, matching the floor required by thereact-on-rails-rsc19.2 package line while preserving the existing React19.0.xsupport window. Refs #3865. #4026 by justin808. -
[Pro]
<RSCRoute>client-control refetch failures are recoverable in production:refanduseCurrentRSCRoute()refetch failures now keep the last successful route content mounted in production, exposerefetchError,retry(), andclearRefetchError()onRSCRouteHandle, and call the optionalonRefetchErrorprop for parent/sibling reporting. Development still fails loudly throughServerComponentFetchErrorso component context and the original refetch error stay visible. Fixes #3565. #3786 by justin808. -
Generator scaffolds the native
RSCRspackPluginfor Rspack RSC projects:rails generate react_on_rails:install --rscon an Rspack app now wires upreact-on-rails-rsc/RspackPlugin(RSCRspackPlugin) instead of the Webpack-compatRSCWebpackPlugin, which produced valid-looking manifests that still broke under Rspack. Re-running the generator on a legacy Rspack config migrates the oldRSCWebpackPluginimport and invocation to the native plugin. Resolves #3488. #3590 by justin808. -
[Pro] Widened the
react-on-rails-rscpeer-dependency range to the full React 19 line:react-on-rails-pronow declaresreact-on-rails-rscas>= 19.0.2 < 20.0.0(previously>= 19.0.2 <= 19.2.3), so futurereact-on-rails-rscpatch and minor releases on the React 19 line no longer trigger a peer-dependency warning. Thereact/react-dompeers stay at>= 16: React 18 support is retained, and the React-19-only RSC path is gated through the optionalreact-on-rails-rscpeer rather than by raising the React baseline. Resolves #3486. #3580 by justin808. -
Generator defaults to Rspack for fresh installs:
rails generate react_on_rails:installandcreate-react-on-rails-appnow default to the Rspack bundler on fresh installs (significantly faster builds via SWC), instead of Webpack. Pass--no-rspack(or its alias--webpack) to use Webpack. This only affects fresh installs — existing apps that already declare anassets_bundlerinconfig/shakapacker.ymlare left unchanged, an explicit--rspack/--no-rspack/--webpackalways wins, and the default falls back to Webpack on Shakapacker versions below 9.0 (where Rspack is unsupported). #3484 by justin808. -
[Pro]
RollingDeployCacheStagernow rejects bundle hashes that start with a hyphen: The sharedReactOnRailsPro::RollingDeploy::SAFE_HASH_PATTERNconstant (also used by the new HTTP rolling-deploy adapter) tightens the cache stager's old local pattern by additionally rejecting leading hyphens. Webpack content hashes never start with-in practice, so this is a no-op for default toolchains, but operators running a custom rolling-deploy adapter that emits hyphen-prefixed hashes will now see those hashes silently dropped from the staged set. If you depend on hyphen-prefixed hashes, rename them to start with an alphanumeric character or_. #3379 by justin808. -
Upgrade contributor pnpm tooling to 10.33.4: The monorepo now pins pnpm 10.33.4 with Corepack's hash-qualified
packageManagerformat, keeps the install-generator CI fallback on the same pnpm version, and relies on the root workspace pin instead of duplicate workspacepackageManagerdeclarations. #3400 by alexeyr-ci2. -
Allow trusted pnpm 10 build scripts in contributor installs: The root workspace now allowlists required native dependency postinstall checks for
@swc/coreandunrs-resolver, sopnpm installunder pnpm 10 no longer skips those trusted build hooks. #3421 by justin808. -
Release publishing now checks
origin/mainCI status before shipping:rake releasenow inspects GitHub Checks fororigin/mainbefore publishing, blocking stable releases on any visible failing or missing checks and prereleases on required checks, with an explicit override path for maintainers. #3407 by justin808. -
[Pro] Updated Pino in the Node Renderer: Raised the
react-on-rails-pro-node-rendererpinodependency range to^9.14.0 || ^10.1.0, aligning with the current Fastify dependency. #3401 by alexeyr-ci2. -
[Pro] Async Rails server deployments use scheduler-scoped renderer clients: Falcon and async-rails deployments can use the async-http renderer client when a long-lived
Fiber.scheduleris already running; renderer clients are reused within that scheduler. Standard Puma streaming uses a per-request scheduler and cleans up the client when the response ends. Seedocs/pro/updating.mdfor the full upgrade guide. #3320 by AbanoubGhadban. -
[Pro] Per-scheduler persistent HTTP connections for Node Renderer:
RendererHttpClientnow reuses HTTP/2 connections across requests within the same Fiber scheduler (Falcon, async Puma), eliminating per-request TCP+TLS+HTTP/2 handshake overhead. Standalone requests (no outer scheduler) continue using ephemeral connections with guaranteed cleanup. The internal connection pool automatically recovers from broken connections without manual eviction. #3428 by AbanoubGhadban. -
[Pro] Migrated Node Renderer HTTP transport from HTTPX to
async-http: React on Rails Pro now usesasync-http(~> 0.95) withio-endpoint(~> 0.17) for all Rails→Node Renderer requests (render, streaming render, asset upload), replacing the previous HTTPX adapter and the customhttpx_stream_bidi_patch.rb. The newRendererHttpClientis a request-scoped client (one client per Rails request — no persistent process-wide pool) and integrates with the length-prefixed wire protocol introduced in #2903. HTTP/2 bidirectional streaming for async props is now provided bypost_bidion the new adapter. Action required for upgraders:config.ssr_timeoutis now a per-read socket timeout applied to each renderer socket read, rather than a task-level timeout wrapping the entire request.config.renderer_http_pool_timeoutis now the TCP connect timeout; post-connect reads are bounded byssr_timeout.- No implicit transport retry for connection drops: drops surface immediately as
ReactOnRailsPro::Error/connection failures. HTTPX previously performed one implicit transport retry; the new adapter usesretries: 0and leaves retry policy to the existing bundle-upload retry loop.
See
docs/pro/updating.mdfor the full upgrade guide. #3320 by AbanoubGhadban. -
[Pro]
PreSeedRendererCacheandPrepareNodeRenderBundlesnow auto-stageloadable-stats.json:ReactOnRailsPro::RendererCacheHelpers.collect_assetsnow appendsloadable-stats.jsonwhenever the file exists on disk, so every caller (rolling-deploy seeding,pre_seed_renderer_cache,pre_stage_bundle_for_node_renderer) stages it automatically. Action required for upgraders: if yourassets_to_copyconfig explicitly listedloadable-stats.json, remove that entry — otherwise you'll see a "Duplicate asset basenames in assets_to_copy" warning on every stage. The duplicate is harmless (stage_assetskeeps the last entry per basename), but the warning is noise. #3173 by justin808. -
[Pro] Unified renderer cache staging:
ReactOnRailsPro::PreSeedRendererCache.call(mode: :copy | :symlink)is now the single entry point for staging the Node Renderer cache. Both modes produce the same<cache>/<bundleHash>/<bundleHash>.jslayout. Thereact_on_rails_pro:pre_seed_renderer_cacherake task acceptsMODE=copy(default; Docker/image builds) orMODE=symlink(same-filesystem). The auto-invocation at the end ofassets:precompiledefaults to:symlink(preserving prior behavior) and now honorsASSETS_PRECOMPILE_RENDERER_CACHE_MODE=copy|symlinkso Docker builds that runrake assets:precompileas the final asset step can opt into copy mode without invoking the rake task separately.MODE=copyraises a clear error when neitherRENDERER_SERVER_BUNDLE_CACHE_PATHnorRENDERER_BUNDLE_PATHis set in non-dev/test environments, because the Node renderer's default lookup can differ from the Ruby side and would silently drop pre-seeded bundles in the wrong directory. The legacyreact_on_rails_pro:pre_stage_bundle_for_node_renderertask andReactOnRailsPro::PrepareNodeRenderBundlesclass remain as deprecated shims that emit a once-per-process warning and delegate tomode: :symlink.react_on_rails:doctorflags deploy scripts that still reference the deprecated task. Heads-up for custom scripts: the previous flat layout wrote$RENDERER_BUNDLE_PATH/<renderer_bundle_file_name>; any external scripts (health checks, renderer launchers) that read that path directly must now read$RENDERER_SERVER_BUNDLE_CACHE_PATH/<bundleHash>/<bundleHash>.jsinstead. #3124 by justin808. -
[Pro] Pro generator now creates the Node Renderer at
renderer/node-renderer.js: The canonical location for the Node Renderer entry point is now a dedicated top-levelrenderer/directory instead ofclient/, making it straightforward to exclude from production Docker builds that strip JS sources after bundling. Docs and Prospec/dummynow use the new path consistently. Existing apps are unaffected — the generator skips files that already exist (including a legacyclient/node-renderer.js). Fixes #3073. #3165 by justin808. -
[Pro] Documentation standardized on
REACT_RENDERER_URLenv var name: The configuration example indocs/oss/configuration/configuration-pro.mdnow showsENV["REACT_RENDERER_URL"]instead of the olderENV["RENDERER_URL"], aligning with the rest of the docs and the generator template. Existing apps that readENV["RENDERER_URL"]in their initializer continue to work — the Prorenderer_urlconfig is whichever env var the user reads in their initializer; no gem code reads either name directly. Rename the env var in your infrastructure configs (and update the initializer to match) if you want to align with the new convention.bin/devnow also warns whenRENDERER_URLis set withoutREACT_RENDERER_URLso the rename doesn't silently fall back to the default renderer URL. #3142 by justin808. -
Length-prefixed streaming wire protocol: The internal protocol between the Rails gems and the Node renderer (and the in-process bundle for the OSS non-streaming path) now uses a length-prefixed framing —
<metadata JSON>\t<content byte length hex>\n<raw content bytes>— instead of wrapping every HTML chunk in a JSON envelope, eliminating ~30% serialize/escape overhead on streamed HTML and correctly handling multibyte content and chunk boundaries. This is an internal transport detail: React on Rails always ships thereact_on_rails/react_on_rails_progems, thereact-on-rails/react-on-rails-pronpm packages, and thereact-on-rails-pro-node-rendereras a matched version set, and the Ruby parser also auto-detects the legacy JSON format, so no application action is required when upgrading all artifacts together. #2903 by AbanoubGhadban. -
react_on_rails:doctorrenderer-cache scan covers CI/CD manifests: The deprecated-task scan that flagsreact_on_rails_pro:pre_stage_bundle_for_node_renderernow also checks.circleci/config.yml,.gitlab-ci.yml,bitbucket-pipelines.yml, every.github/workflows/*.yml/.yaml, and everyconfig/deploy/*.rbstage file, on top of the existing Procfile/Dockerfile/Compose/Kamal/Capistrano/bin/*/scripts/deploy.shpaths. The scan stays bounded: per-file size cap, no**globs, a per-glob match cap, per-file rescue, and a separate per-glob rescue so a single unreadable workflow or stage file cannot abort the rest of the scan. Fixes #3247. #3329 by justin808. -
Rspack install scaffolding now targets Rspack v2:
react_on_rails:install --rspackandbin/switch-bundlernow generate the Rspack v2 package line (@rspack/core@^2.0.0-0,@rspack/cli@^2.0.0-0,@rspack/plugin-react-refresh@^2.0.0) while keepingrspack-manifest-plugin@^5.0.0, which is already compatible. Closes #3082. #3084 by justin808.
Improved
- [Pro] Embedded RSC payloads no longer repeat full serialized props in the page HTML: The embedded RSC payload cache key now uses a compact hash of the component props instead of the full
JSON.stringify(componentProps), so server-rendered pages with RSC components stop repeating large props JSON once per Flight chunk in render-blocking inline scripts (a 1KB props object across 10 chunks previously added ~12KB of repeated JSON; now ~120 bytes). The client-sideRSCProviderin-memory promise cache still keys on full props, so payload deduplication behavior is unchanged. Fixes #3796. #3800 by AbanoubGhadban. - [Pro] RSC manifest setup avoids synchronous file-stat work on render: Pro RSC rendering no longer relies on synchronous
fs.statSyncmanifest signature checks during render-time setup, avoiding Node event-loop blocking on that path. The manifest-wide CSS/link cache path that used those signatures was later removed by #3860. #3595 by justin808. - Server-bundle load failures are now classified separately from renderer-connection failures: Bundle read/fetch failures raise the new
ReactOnRails::ServerBundleLoadErrorinstead of being grouped with renderer-connectivity errors, keeping the renderer-connection classification (see #3614) focused on render-origin connection failures, including wrappedconnect(2)errors found through the exception cause chain. Fixes #3628. #3724 by justin808. - Renderer connection failures are no longer misreported as webpack/bundle errors: When server rendering fails because Rails cannot reach the renderer process (for example a Pro Node renderer behind
REACT_RENDERER_URLthat refuses the connection —ECONNREFUSED,EHOSTUNREACH,ETIMEDOUT, or a sandboxedErrno::EPERM/connect(2)failure), React on Rails now raises a renderer-connectivity error that names the host/port it tried to reach and points atREACT_RENDERER_URLand renderer liveness, instead of the misleading "Error evaluating server bundle. Check your webpack configuration." The existing webpack/server-bundle troubleshooting is retained for genuine bundle evaluation errors. Fixes #3604. #3614 by justin808. - RSC setup verification warns on dynamic plugin options:
react_on_rails:install --rscnow warns whennew RSCWebpackPlugin(...)uses computed options that cannot be statically verified, avoiding misleading missing-clientReferencesreports for dynamic config. Fixes #3412. #3505 by justin808. - Rspack-aware diagnostics and dev-server help: Doctor, system checker, and
bin/dev --helpoutput now label Rspack apps as Rspack instead of webpack while preserving webpack wording for default apps. Fixes #3388. #3508 by justin808. - Resolved Shakapacker config path warnings now show the expanded path: Missing
SHAKAPACKER_CONFIGwarnings now include the Rails-root-resolved path that was checked, making relative-path typos easier to diagnose. #3444 by justin808.
Fixed
-
[Pro] Failing RSC payloads no longer cause unbounded browser requests:
RSCProvidernow
keeps one cached Promise for a logical payload load and retries a transient network, server, or
malformed-payload failure once within it. If the retry also fails, React receives the final
rejection instead of repeatedly starting new requests. The retry bypasses failed embedded
payloads and fetches fresh data; 4xx responses and cancellations are not automatically retried,
and official server rendering does not perform the browser retry. A later browser lookup may try
again after a short retention window. Fixes
react_on_rails_rsc Issue 187.
#4564 by
ihabadham. -
[Pro] Production streamed RSC CSS reveal gating: Pro streaming now promotes stylesheet
preloads listed in the React client manifest, preventing a flash of unstyled content when
production chunk CSS uses numeric IDs and id-named files. Fixes
#4568.
#4570 by
justin808. -
[Pro] RSC payload prerender cache no longer stores an empty payload: With
config.prerender_caching = true, the RSC payload endpoint (/rsc_payload/:component_name) served
the first visitor a correct payload but every subsequent visitor a zero-byte payload, because the
length-prefixed framing consumer destructively removed"html"from the chunk Hash that
StreamCachehad buffered by reference and wrote to the cache after the stream completed. The
browser's Flight client then rejected the empty response with"Connection closed.". The framing
consumer now reads"html"without mutating the chunk, andStreamCachesnapshots each chunk
before handing it downstream so a mutating consumer can never corrupt the cached entry. Fixes
#4550.
#4551 by
AbanoubGhadban. -
[Pro] Deferred RSC route failures now reach app error boundaries: Rejected deferred RSC route
fetches stay observable long enough for React error boundaries to render their fallback, while failed
entries release their in-flight cache pin without evicting unrelated healthy payloads under concurrent
load. Fixes #4522.
#4529 by
justin808. -
[Pro] RSC doctor and version checks resolve Rspack from the configured app dependencies:
RSC artifact diagnostics and startup version checks now resolve@rspack/corefrom the configured
clientnode_modulespath, avoiding false Rspack verification failures when the app's package root
differs from the current process context. Fixes
#4523.
#4530 by
justin808. -
hydrate_on: visibleno longer leaks a detached root or blocks re-hydrating a replacement node: When ahydrate_on: visibletarget was detached from the DOM before it became visible, the intersection observer left a stale scheduled root entry that kept the removed node reachable and could stop a replacement node with the same DOM id from scheduling its own hydration. The observer now runs its scheduled callback after disconnecting from a detached target; the callback's existingisConnectedguard deletes the stale entry without hydrating the removed node, so a fresh node with the same id schedules cleanly. Fixes #4328. #4374 by justin808. -
[Pro] RSCRoute error recovery now covers synchronous route failures and fire-and-forget retries: Synchronous throws from RSC route fetch/render setup are normalized into rejected fetch promises so they flow through the same
RSCRouteerror-recovery path (andRSCRouteErrorBoundary) as async fetch failures instead of bypassing it, keeping retry/refetch bookkeeping consistent when a fetch fails synchronously. Fire-and-forget recover-on-error retries also no longer surface as unhandled promise rejections in production: the caller-visible retry promise stays rejected for callers that await or catch it, while an internal no-op handler prevents an unhandled rejection when the retry is not awaited. Fixes #4330 and #4372. #4378 and #4393 by justin808. -
[Pro] Node renderer rejects unsafe asset filenames: The Pro Node renderer now rejects path-like,
drive-relative, alternate-stream, and control-character asset filenames before uploaded assets are copied
or/asset-existsprobes bundle directories, returning a 400 without reporting rejected names to the
error reporter. #4285 by
ihabadham. -
[Pro] Streamed RSC responses now append the Node renderer's
ror_renderer_prepare
metric to the browser-visibleServer-Timingheader while preserving existing
Rails/application entries, sorsc_stream_observabilityexposes renderer prepare
time without relying on HTTP trailers. Closes
#4479.
#4487 by
justin808. -
[Pro] Streaming component caches honor
cache_optionson reads:cached_stream_react_component
wrote cached chunks with the user-suppliedcache_optionsbut read them back without any options, so
key-altering options such asnamespace:meant the cache never hit and every request re-streamed from
the renderer (a bare read could even return a stale un-namespaced entry for the same logical key). Reads
now pass the same normalized options as the deferred write, and the lower-level streaming prerender cache
(ReactOnRailsPro::StreamCache.fetch_stream) likewise accepts and applies thecache_optionsits write
path uses. #4452 by
ihabadham. -
[Pro] Sync RSC route failures surface as
ServerComponentFetchError: Synchronous throws
in the RSC payload path (payload key creation on BigInt/circular props, sync
getServerComponentthrows,fetchRSCrequest preparation) previously bypassed
RSCRouteErrorBoundary, so user error boundaries received rawTypeErrors without component
metadata. All sync and async failure paths now funnel through the boundary, and a nested route's
already-wrapped error passes through without re-wrapping. Fixes
#4372.
#4438 by
justin808. -
[Pro] RSC payload streaming flushes complete HTML before incomplete tails: The Pro RSC
payload injector now streams the complete prefix of each chunk immediately while retaining only
incomplete UTF-8 sequences, HTML tags, comments, template content, raw-text elements, or
foreign-content tails for the next chunk. This preserves progressive streaming when a chunk ends
in partial markup instead of holding the entire flush until the tail completes. Fixes
#4327.
#4379 by
justin808. -
[Pro] Truncated RSC parser streams now warn at EOF: The Pro RSC length-prefixed stream
parser flushes at stream end so incomplete trailing records emit the existing parser warning,
while expected request-abort cleanup remains quiet.
#4392 by
justin808. -
ReactOnRails.getStore(name, false)no longer throws when no stores are hydrated: With
throwIfMissing = false,getStorenow returnsundefinedwhen the hydrated-store registry is
empty — matching its documented contract and its existing behavior when other stores are hydrated —
in both the open-source and Pro JS packages. Default strict calls still throw the descriptive
"There are no stores hydrated" / "Could not find hydrated store" errors.
#4457 by
ihabadham. -
[Pro] RSC stylesheet inference retries after transient stats read failures: Pro now caches
client-chunk stylesheet metadata only after a successfulloadable-stats.jsonread, so a
deploy-race or temporary parse/read failure does not disable inferred RSC client stylesheets for
the worker lifetime.
#4401 by
justin808. -
[Pro] RSC loadable-stats retry diagnostics stay visible: Malformed or unreadable
loadable-stats.jsonwarnings are suppressed only for the retry window, and native ESM stack
paths rewritten through source maps are normalized back to the runtimelib/directory before
resolving stats.
#4447 by
justin808. -
[Pro] RSC client fetch failures no longer include serialized props in thrown error messages or causes:
Browser RSC requests still send the real?props=...payload to the Rails endpoint, but pre-response fetch
failures now report the query-free component source path with a stable generic message so console logs, error
boundaries, and error reporters do not capture accidental sensitive values from props. Retry props remain
directly readable for refetches but are no longer enumerable error fields. Fixes
#4449.
#4450 by
ihabadham. -
[Pro] Node renderer graceful shutdown and scheduled restarts: Worker shutdown now counts
each active request once across response, abort, and timeout hooks; scheduled restart timeouts use
documented seconds; draining workers skip only the early master SIGKILL while keeping the hard
shutdown deadline; and stale scheduled-restart workers no longer break the restart loop. Fixes
#4365,
#4366,
#4367, and
#4368.
#4400 by
justin808. -
[Pro] Response-start send failures are reported during abandoned incremental renders:
The Pro node renderer now observes and reports rejected response-start/send promises when
incremental render request handling stops early or errors after a response starts, while still
propagating those failures on clean stream completion. Fixes
#4364.
#4389 by
justin808. -
[Pro] Async-props prerender stream cache isolation: Pro prerender stream caching now
bypasses renders that use async props, so per-request async stream output cannot be replayed from
another request's cached stream. Fixes
#4359.
#4376 by
justin808. -
Preload links stay compatible with older Shakapacker:
react_on_rails_preload_linksnow
skips SRI attributes when Shakapacker does not expose integrity settings, avoiding a
NoMethodErrorwhile still emitting preload hints. Fixes
#4369.
#4377 by
justin808. -
[Pro] Renderer HTTP transport no longer buffers successful streams or duplicate bundle uploads:
Pro renderer requests now pass successful streaming response chunks through without retaining them in Ruby
memory, reuse a persistent async-http client for plain Puma non-streaming requests, reset scheduler-scoped
clients when the shared renderer connection is reset, and stream/deduplicate same-bundle uploads. Plain Puma
reuse is per request-handling thread, so total renderer connection capacity can scale with Puma threads times
renderer_http_pool_size; tune the renderer and any intermediaries accordingly. Fixes
#4360,
#4361,
#4362, and
#4363.
#4394 by
justin808. -
[Pro] Streaming dependency load errors stay visible: Pro streaming cleanup now tolerates dependency
load failures that happen before stream observability state is captured, so the originalLoadError
remains visible instead of being masked by cleanup. Fixes
#4324.
#4388 by
justin808. -
[Pro] Tag revalidation keeps retry metadata after entry delete failures:
ReactOnRailsPro.revalidate_tagnow restores the tag index before re-raising when tagged
cache-entry deletion fails, so transient cache-store failures leave retry metadata instead
of orphaning stale entries until their TTL expires. Fixes
#4317.
#4375 by
justin808. -
hydrate_on: nilfalls back to immediate hydration: Passinghydrate_on: nilnow behaves
like the default:immediatemode instead of raising, while invalid explicit values still fail
fast. Fixes #4342.
#4350 by
justin808. -
[Pro] Incremental render setup failures release renderer context:
The Pro node renderer now releases the execution context and destroys started streams when
pull-mode incremental render setup fails, preventing orphaned renderer work after response-start
errors. Fixes #4312.
#4383 by
justin808. -
[Pro] Dropped pull-mode prop requests are logged: Streaming SSR now warns when the Node
renderer sends apropRequestcontrol frame without an emitter or with a missing, empty, or
oversizedpropName, making dropped pull-mode requests diagnosable while preserving valid
enqueue andrenderCompletebehavior. Fixes
#4314.
#4352 by
justin808. -
[Pro] RSC preload replay survives cache eviction: Preloaded RSC payloads now remain
replayable after route cache eviction, preventing stale preload state from breaking refetches.
Fixes #4326.
#4353 by
justin808. -
[Pro] Incremental stream timers stay aligned with active chunks: Healthy pull-mode
incremental streams are no longer closed by stale request or finish timers while chunks keep
progressing, while abandoned streams remain bounded by an idle watchdog. Fixes
#4310 and
#4311.
#4354 by
justin808. -
[Pro] RSC Rspack boot validation warns on undetermined versions: Boot now warns and continues when
the active Rspack version cannot be determined, while strict doctor checks and provable Rspack v1
failures still fail closed. Fixes #4340.
#4355 by
justin808. -
[Pro] Registry cleanup handles page-unload cancellations safely: Pending component and store
registry waits are now rejected when a page unloads or store generators are cleared, stale registry
timeouts are cleared, and expected navigation cancellations are ignored during client rendering while
real registration failures still surface. #4282
by justin808. -
[Pro] Gemfile loader source encodings are honored under C/POSIX locales:
The Pro Gemfile now loads its shared dependency fragments in binary mode, applies Ruby
source-encoding magic comments or a UTF-8 default, and validates content before override-gem
scanning and evaluation. This affects any environment that evaluates the Pro Gemfile through
Bundler, including local setup and CI, so Pro apps with non-ASCII dependency comments no longer
fail under shells where Ruby's default external encoding is US-ASCII. Fixes
#4276.
#4281 by
justin808. -
[Pro] Cached component hits load generated packs consistently:
Pro cached component helpers now run the cache-hit pack-loading path consistently, so cached
cached_react_componentandcached_react_component_hashoutput preserves generated pack behavior. Fixes
#4316.
#4384 by
justin808. -
Precompile hook no longer forces UTF-8 onto a non-UTF-8 locale:
The shared Shakapacker precompile hook now widens a spawnedbundle exec/ shakapacker subprocess
to UTF-8 only under a bare C/POSIX locale, where the locale-derived encoding is US-ASCII — a
strict subset of UTF-8, so the widening cannot corrupt genuinely-ASCII content. Under a real
national locale (for example a Brazilian developer'sLANG=pt_BR.ISO8859-1) it now leaves
LANG/LC_ALL/RUBYOPTuntouched and lets the child inherit the working locale, instead of
force-pinning-EUTF-8and re-decoding the developer's latin-1/CP1252 source files as UTF-8 (which
raisedinvalid byte sequence in UTF-8). The locale gate readsEncoding.find("locale")so it is
not masked by Rails settingEncoding.default_externalto UTF-8 at boot. This removes the
RUBYOPT-rewriting machinery added in
#4231 while keeping the original
C/POSIX-locale crash fix from
#4169.
#4244 by
justin808. -
[Pro] RSC Rspack doctor no longer false-warns on equivalent
lazyCompilationconfigs:
react_on_rails:doctor:rsconly recognizes the generated literal
clientWebpackConfig.lazyCompilation = falseassignment. Apps that disable lazy
compilation an equivalent way (object form,Object.assign, a helper, a ternary,
or a different config file) now get a warning that says doctor could not confirm
the setting rather than asserting lazy compilation is still enabled, plus guidance
to confirm the effective dev-server config and ignore the warning if it is already
disabled. Follow-up to
#4234.
#4249 by
justin808. -
Generated Tailwind apps load Tailwind from the layout. The install generator now declares Tailwind through a layout-owned pack instead of component-owned imports, keeps generated layout head metadata mobile/CSP-ready, and warns safely when custom layouts need manual pack-tag replacement. #4182 by ihabadham.
-
[Pro] Rspack RSC dev-server setup is easier to diagnose and customize:
Generated RSC helper code now verifies client-reference discovery support
through the siblingrscWebpackConfig.jsfile instead of assuming
config/webpack, so Rspack apps keep usingconfig/rspack/rscWebpackConfig.js.
The RSC doctor also warns existing Rspack apps when normalbin/devcan leave
the React Client Manifest empty becauselazyCompilationis still enabled,
and the troubleshooting guide documents thelazy-compilation-proxy/
POST /_rspack/lazy/trigger404 symptom path. Follow-up to
#4227.
#4234 by
justin808. -
Precompile hook UTF-8 hardening now handles conflicting
RUBYOPTencoding flags:
The shared Shakapacker precompile hook strips pre-existing Ruby encoding flags such as
-EUS-ASCII,--encoding=US-ASCII,--external-encoding=US-ASCII, and
--internal-encoding=US-ASCIIbefore pinning subprocesses to-EUTF-8:UTF-8, so C/POSIX-locale
builds do not crash when the parent shell exports a conflictingRUBYOPT. The shipped generated
bin/shakapacker-precompile-hooktemplate now uses the same UTF-8 subprocess environment for RSC
client-reference discovery. Follow-up to
#4169.
#4231 by
justin808. -
[Pro] Generated RSC + Rspack apps render in normal
bin/dev:
RSC + Rspack generator output now disables Rspack lazy compilation while the
dev server is running, so discovered client references are compiled before
the React Client Manifest is read during server rendering. Fresh generated
apps no longer fail/hello_serverwith an emptyreact-client-manifest.json
in normal HMR mode. Fixes Issue 4226.
#4227 by
ihabadham. -
[Pro] RSC doctor now catches stale install and client-manifest setup failures:
The doctor now validates installedreact-on-rails-rscpeer requirements
againstreactandreact-dom, warns when the installed RSC package is
behind newer prerelease npm dist-tags, and reports missing, dev-server-backed,
invalid, or empty RSC client manifests withbin/dev staticand clean rebuild
guidance. Pro RSC render errors that fail to resolve a React Client Manifest
module now include the same stale/empty/cross-version manifest hint instead of
leaving the upstream "probably a bug in the RSC bundler" text as the only clue.
Fixes #4198
and #4200;
addresses #4199
and scopes #4204.
#4213 by
justin808. -
Dummy app setup uses native Nokogiri gems on macOS: The dummy app lockfile now includes Darwin platforms so
bin/setupinstalls native Nokogiri gems on Apple Silicon and Intel macOS instead of attempting a source build that can fail with a missingnokogiri_gumbo.hheader. #4218 by justin808. -
[Pro] ScoutApm Node renderer instrumentation no longer depends on Gemfile load order: Pro now installs
NodeRenderingPool.exec_server_render_jsinstrumentation from a Rails engine initializer that runs afterscout_apm.start, instead of checkingdefined?(ScoutApm)at class load time. Apps without ScoutApm still boot normally, and apps that loadscout_apmafterreact_on_rails_prono longer silently skip the Pro Node renderer span. Fixes #4208. #4210 by justin808. -
[Pro] RSCProvider now evicts a rejected
getComponentpromise so transient failures can retry: When the underlying RSC fetch forgetComponentrejected — a transient renderer/network/deploy failure — the rejected promise stayed in the provider's bounded payload cache, so every later same-keygetComponentreturned that cached rejection and the RSC route/component stayed wedged in its error state even after the backend recovered; only an explicitrefetchComponentor a full reload cleared it.getComponentnow attaches a rejection handler that re-throws (so React's Suspense machinery still observes the failure) and evicts the cached entry one macrotask later, guarded on promise identity so a newer same-keygetComponent/refetchComponentstarted in that window is never evicted by the stale failure. Pins are preserved so the existing.finally()still owns the matching unpin. Payloads that resolve with anErrorvalue are intentionally left cached — that retryability is a separategetServerComponentcontract decision. Fixes #3929. #4189 by justin808. -
[Pro] Precompile hook no longer crashes under a non-UTF-8 (C/POSIX) locale: The shared Shakapacker precompile hook now forces a UTF-8 locale on every
bundle exec/ shakapacker subprocess it spawns — pack generation, the i18n locale generation added in #4128, and the RSC client-reference discovery build. WithoutLANG/LC_ALLset, those children inherited a US-ASCII default external encoding and died parsing Gemfiles containing non-ASCII bytes (e.g.react_on_rails_pro/Gemfile.loader:invalid byte sequence in US-ASCII), aborting the entire precompile. Extends the UTF-8 hardening from #3949 from the hook's own file reads to the subprocess boundary. #4169 by justin808. -
OSS renders no longer compute or emit Pro-only generated-stylesheet metadata: Rendering a component
now skipsgenerated_stylesheet_hrefs_json's stylesheet-href discovery entirely when React on Rails Pro
is not installed, instead of running that Pro-only work on every open-source render. Fixes
#4341.
#4395 by
justin808. -
create_render_optionsno longer mutates the caller's options hash: Adding default store
dependencies and internal stream-observability state now operates on a duplicate of the supplied
options, so a frozen or reused options hash passed to a helper no longer raisesFrozenErroror leaks
observability state into a later render that reuses the same hash. Fixes
#4343.
#4396 by
justin808. -
Locale-file regeneration check no longer reads the whole generated file, and correctly detects a
legacy import after custom content: The obsolete-locale-file check now streamsdefault.jsline by
line, stopping as soon as it finds either the legacyreact-intl/defineMessagesimport (regenerate)
or the currentconst defaultLocale = ...marker (up to date), instead of reading and matching the
entire file from the start. This also fixes a false negative where a legacy import following custom
file content was not detected. Fixes #4345.
#4398 by
justin808. -
[Pro] Static RSC payload script stripping is robust to generated body-shape changes: Pro-generated
RSC initialization, payload-chunk, and diagnostic<script>tags now carry an explicit
data-react-on-rails-rsc-payload="true"marker, and static RSC cache stripping
(cached_static_rsc_component) now prefers that marker over inferring payload scripts from body shape,
while still falling back to the previous shape-based detection for older generated HTML. Fixes
#4459.
#4477 by
justin808. -
Deferred hydration error reporting handles non-Error thrown values: Delayed
hydrate_onrenders now normalize strings,null, and frozenErrorinstances before logging, so reporting the failure does not throw again or mutate user errors. #4120 by ihabadham. -
Explicit Webpack installs now pass the resolved bundler to Shakapacker.
rails generate react_on_rails:install --no-rspackand--webpacknow setSHAKAPACKER_ASSETS_BUNDLER=webpackbefore runningshakapacker:install, so Shakapacker installs Webpack dependencies instead of falling back to its default bundler. Fixes #4108. #4109 by ihabadham. -
Generated demo paths now honor custom Shakapacker source roots. The install generator resolves demo components, entrypoints, stylesheets, TypeScript includes, Tailwind imports, and RSC hints from the app's Shakapacker
source_path/source_entry_pathsettings, including slash entry roots, while wrapping long source hints in the generated demo views. Fixes #4062. #4107 and #4130 by justin808. -
RSC-safe generated i18n locale defaults: The JavaScript locale compiler that generates
default.jsno longer importsreact-intlor wraps messages indefineMessages; it now emits the message descriptor object directly. This lets the generated locale defaults be imported from React Server Component bundles without pulling in the client-orientedreact-intlentrypoint, and without raising the minimum supportedreact-intlversion. The exporteddefaultMessagesshape is unchanged, and existing apps regenerate automatically because the compiler treats adefault.jsstill using the olddefineMessagestemplate as stale. Fixes #4132. #4146 by justin808. -
Abort the in-flight SSR render when the client disconnects (Pro streaming): Previously, when an HTTP client disconnected (or a request timed out) mid-stream, the Node renderer kept driving the React render to completion against a consumer that was already gone — wasting CPU and, for RSC/
cache()-wrapped data fetches, continuing to hit the app's database/APIs. The Pro streaming layer now propagates the consumer-side teardown upstream into ReactDOM'sPipeableStream.abort(): when the renderer worker detects the client disconnect it destroys the render's output stream, which aborts the in-flight render and releases the request's RSC payload streams. Normal completion is unaffected (the abort only fires when the output is destroyed before it ends, and never when a render error closes the stream). This also establishes the precondition for React 19.2'scacheSignal, which React settles automatically once a render is aborted (thecacheSignal-specific test and docs remain a follow-up). Part of #3885. #4093 by justin808. -
[Pro] Bounded the RSCProvider RSC payload cache to prevent unbounded growth under high-cardinality props: The provider-scoped promise cache (
fetchRSCPromisesRef) and its companion bookkeeping (lastSuccessfulRSCPromisesRef, refetch versions, and theversions/successfulVersionsstate maps) are now backed by a bounded LRU (default cap 50 distinct RSC payload keys). High-cardinalitycomponentProps(e.g. per-row or per-search-query routes) previously grew these maps without limit for the provider's entire lifetime — a latent memory leak. Eviction only affects cold, least-recently-used keys beyond the cap; same-key cache hits, refetch,recoverOnErrorrestore, and version bumping are unchanged, and an in-flight refetch's key is pinned (with ref-counted pins, so overlapping same-key refetches stay protected until all of them settle) and cannot be evicted out from under its restore path. The per-keyuseSyncExternalStoresubscription/fan-out optimization from the same issue is intentionally deferred pending profiling. Refs #3564. #4097 by justin808. -
[Pro] Enrich deferred-render RSC errors with the bundle diagnostic: When a Server Component failed during React's deferred render phase (a Suspense boundary resolving a lazy RSC element), the error surfaced through
renderToPipeableStream'sonErroras a generic React stream error — the original RSC bundle diagnostic (the real server-side error message and module path) was already out of scope and lost. The Pro streaming layer now threads the captured diagnostic through the request-scoped tracker and merges it into the surfaced error, soReactOnRails::PrerenderError/SmartErroroutput names the failing RSC component and module instead of a bare React message. Since React'sonErrorcarries no component key, attribution is conservative: one captured diagnostic is merged exactly, two or more produce a combined "one of these N RSC components failed" message (never a single false pinpoint), and each captured diagnostic is consumed on first use so an unrelated later failure in the same render is never mislabeled. Completes the deferred-render half of the bundle-diagnostic work (the fetch and preloaded-hydration halves shipped earlier). Closes #3475. #4100 by justin808. -
[Pro] RSC and client-only FOUC reveal gating: Pro now waits for auto-loaded generated component stylesheets before mounting client-only roots and promotes streamed RSC client chunk stylesheet preloads to real stylesheet links before reveal, including when stream chunks split
<link>tags. Shared generated CSS is matched through manifest-derived stylesheet href metadata, while app-authored preload links remain untouched. Fixes #4031, #4032. #4047 by justin808. -
Rspack generated apps start in HMR mode: Fresh
rails generate react_on_rails:install --rspackandcreate-react-on-rails-appprojects now install@rspack/dev-server, use theReactRefreshRspackPluginexport, and keepbin/switch-bundler rspack's dev dependencies complete sobin/devcan launch Rspack serve instead of crashing during dev-server startup. Fixes #3925. #3926 by AbanoubGhadban and ihabadham. -
[Pro] Node renderer drains workers on
SIGTERM/SIGINTinstead of orphaning them: The Pro Node renderer master now installsSIGTERMandSIGINThandlers before forking workers, so supervisors (Foreman, local dev, process managers) that signal the master gracefully drain workers — sending the shutdown message, waiting a short grace period, callingcluster.disconnect(), then awaiting each worker'sexitevent — instead of killing only the master and leaving orphaned worker processes behind. Shutdown uses signal-style exit codes (SIGINT→ 130,SIGTERM→ 143), reuses a shutdown-time worker snapshot for the hardSIGKILLfallback so disconnected-but-still-running workers are not missed, and suppresses reforks while a shutdown is in progress. Fixes #3863. #3882 by justin808. -
[Pro] RSC
unstable_cacheno longer caches failed Flight renders: React Flight render errors reported byrenderToPipeableStreamnow skip the cache write, so later requests retry the render instead of replaying a failed RSC payload from cache. React's default error logging behavior is preserved. Fixes #3774. #3775 by ihabadham. -
[Pro] Prerender cache no longer misses when only the random
domNodeIdchanges: Pro prerender cache digest normalization now stripsdomNodeIdvalues emitted in JSON/double-quote form as well as the older single-quote form, so cached prerender results are reused across requests that differ only by the randomly generated DOM node id, while changed props still produce distinct digests. Fixes #3706. #3707 by ihabadham. -
Auto-bundled snake_case component names now load their generated packs consistently: Auto-bundled component pack loading is normalized through the same name resolution used for DOM/SSR lookup, so
react_component("component_name")loads thegenerated/ComponentNamepack, and public component names generated for files under the configured auto-bundling components subdirectory are camelized (server-bundle and store filename behavior is preserved, as is generated-pack conflict protection for names differing only by case). Fixes #3809. #3818 by justin808. -
[Pro] A throwing React root unmount no longer aborts client teardown: The Pro client renderer now guards the modern React root
unmount()path, logging failures atconsole.errorand always clearing the tracked root, so one failing root unmount cannot stop other components from unmounting or block later renders on the same DOM node. Fixes #3618. #3716 by justin808. -
[Pro] Plain-object component registrations keep their type in the Pro
ComponentRegistry: Pro registry entries are now typed to cover plain object modules used byserver_render_js(matching the core package types from #3606),getOrWaitForComponentaligns with the widened entry type, and the Pro client renderer now guards against invoking non-callable renderer entries. Fixes #3677. #3719 by justin808. -
Server-component wrapper types reject non-component render functions at compile time: The new
ReactComponentRenderFunctiontype models render functions that must return a React component, and the ProwrapServerComponentRendererclient/server inputs are narrowed to it, so registering a teardown-returning renderer function there is now a TypeScript compile-time error instead of relying only on the existing runtime guards (which are unchanged for JavaScript callers). Fixes #3589. #3720 by justin808. -
[Pro] Incompatible
react-on-rails-rsc/ React versions now fail loudly instead of silently misbehaving: the Pro node renderer checks the installedreact-on-rails-rsc,react, andreact-domversions at boot and throws a clear error when they are outside the supported React 19.2.x line. SetREACT_ON_RAILS_PRO_DISABLE_VERSION_CHECK=1to downgrade the hard error to a warning. #3831 by justin808. -
[Pro] RSC
React.cache()request dedupe now works in generated configs: Generated RSC webpack configs now canonicalizereact,react/jsx-runtime, andreact/jsx-dev-runtimeresolution to one React server package instance, keeping React's RSC cache dispatcher shared between the renderer and app Server Components. This restores request-localReact.cache()memoization for direct Server Component data loaders in generated RoRP RSC apps. Fixes #3812. #3813 by ihabadham. -
[Pro] Preloaded RSC hydration errors preserve bundle diagnostics: RSC payload injection now threads the server-side
renderingErrormetadata to the browser so preloaded hydration failures can report the original RSC bundle error message and module path, matching the fetch path while avoiding unrelated stream metadata such as serialized props. Addresses the preloaded hydration portion of #3475. #3766 by justin808. -
Auto-bundled component name conflicts now fail loudly: React on Rails now raises an error when multiple auto-bundled component files resolve to the same public component name, listing the conflicting files instead of silently keeping whichever path overwrote the earlier mapping. Fixes #3708. #3709 by ihabadham.
-
TypeScript source server bundles work with auto-generated packs: React on Rails now resolves the configured server bundle source entrypoint by extension, so apps can keep
config.server_bundle_js_file = "server-bundle.js"as the compiled/runtime bundle name while using a TypeScript source entrypoint such aspacks/server-bundle.ts. Public registration types also now cover plain object modules used byserver_render_js, matching existing runtime behavior. Resolves #1583. #3606 by ihabadham. -
[Pro] Client teardown failures are no longer hidden at
console.info: whenComponentRenderer.unmount()catches an error fromunmountComponentAtNode(the React 16/17 legacy unmount path), it now logs atconsole.errorinstead ofconsole.info. A caught error there means the component tree did not unmount cleanly — a teardown failure — and most log collectors and default browser-console filters dropinfo, so the failure was effectively silent. Addresses item 2 of #3592. #3610 by justin808. -
Renderer functions no longer leak their mount on navigation/unmount: Renderer functions (the 3-argument
(props, railsContext, domNodeId) => …registration form) own their own React root, but React on Rails never tracked any cleanup state for them, so every renderer-function mount leaked on Turbo/Turbolinks navigation. Renderer functions may now optionally return a teardown wrapper ({ teardown: () => void | Promise<void> }, sync or async); returning nothing keeps the previous behavior, so existing renderers are unaffected. Both the core and Pro client renderers invoke the teardown on page unload and on same-id node replacement, and cleanup failures on same-id replacement are now logged toconsole.errorinstead of only being visible when tracing is enabled. The renderers differ only in the async race: if a navigation unmounts the mount while an async renderer is still resolving its teardown, Pro still runs the teardown once it resolves, whereas the core renderer is best-effort and may drop a still-pending async teardown while the renderer is awaiting dynamic imports, fetches, or other I/O on a fast navigation; active async renderer failures are logged and then untracked so a later load call can retry. The framework-shipped ProwrapServerComponentRenderernow returns such a teardown wrapper, closing the leak automatically for everyregisterServerComponentuser. TypeScript note: the exportedRendererFunctiontype covers 3-argument renderers that return nothing or an optional teardown wrapper;RenderFunctionkeeps its existing component/server-result return contract, including legacy 3-argument renderers that returned a component only to satisfy the old type. Fixes #3209. #3576 by justin808. -
Shakapacker config warnings now report resolved relative paths: When
SHAKAPACKER_CONFIGis set to a relative missing path, the Rails boot warning now includes the Rails-root-resolved path that React on Rails actually checked. Fixes #3436. #3441 by justin808. -
Base-port renderer URLs preserve localhost-equivalent hosts:
bin/devbase-port mode now keeps localhost-equivalent renderer hosts and schemes, such as127.0.0.1andhttps://localhost, when derivingREACT_RENDERER_URL; remote or invalid hosts still fall back tohttp://localhost:<port>. Fixes #3466. #3506 by justin808. -
[Pro] Streamed RSC rendering now propagates CSP nonces: React on Rails Pro now passes the Rails CSP nonce to React's streamed RSC renderer options so streamed script output can satisfy strict content security policies. Fixes #3491. #3507 by justin808.
-
[Pro] RSC stream parser tolerates blank separator lines:
LengthPrefixedStreamParsernow skips blank separator lines (including a lone CR from a split CRLF) between length-prefixed records instead of treating them as malformed headers, matching the default Pro RSC payload template that inserts extra newlines between chunks. Malformed non-empty headers still raise as before, and a stream ending mid-\r\nno longer logs a spurious incomplete-stream warning. Fixes #3499. #3515 by justin808. -
Generated build scripts run the auto-bundle hook before building bundles: Newly generated apps now run the Shakapacker precompile (auto-bundle) hook before the generated
build,build:test, scaffolded CI, andbuild_test_commandbundle builds, so packs are regenerated before bundling. Apps with custom Shakapacker hooks keep them under Shakapacker's control so the hook does not double-run. #3535 by justin808. -
[Pro] RSC client-hook runtime errors now explain the missing client boundary: React on Rails Pro now rewrites RSC runtime hook failures such as
useState is not a functionwith a diagnostic that names the registered component, points to the likely missing"use client";directive, and clarifies that.client/.serversuffixes only control bundle placement. Fixes #3184. #3461 by justin808. -
Test asset compiler output is now bundler-neutral: Test asset compilation now prints "Building assets..." and "Completed building assets." instead of Webpack-specific wording, and failure guidance tells users to rerun their configured
build_test_commandinstead of naming Shakapacker. Fixes #3455. #3462 by justin808. -
Test helper dev-asset guidance is now bundler-neutral: HMR warnings, missing generated-file guidance, and test-helper comments now refer to the configured bundler/dev server instead of Webpack or Shakapacker-specific commands while keeping the existing public
WebpackAssets*API names. Fixes #3478. #3513 by justin808. -
[Pro] RSC stream failures now surface original diagnostics: RSC payload stream metadata now preserves the original RSC bundle exception message, stack, component name, and module path where available across server-bundle rendering, Rails
PrerenderError, and browser fetch paths. Fixes #3182. #3463 by justin808. -
[Pro]
react_on_rails:doctorrenderer-cache scan now covers Jenkinsfile: The deprecated-task scan that flagsreact_on_rails_pro:pre_stage_bundle_for_node_renderernow also checksJenkinsfile, alongside the existing CI/CD manifests and deploy scripts. Fixes #3269. #3442 by justin808. -
Client-only Vite setups no longer fail Rails boot on Shakapacker's
packageManagerguard: React on Rails now installs an engine initializer that runs beforeshakapacker.manager_checkerand no-ops Shakapacker'serror_unless_package_manager_is_obvious!when the host app has no Shakapacker config (config/shakapacker.ymlorSHAKAPACKER_CONFIG). This unblocks apps that use thereact-on-rails/clientnpm package from an existing Vite entrypoint and do not use the Ruby render helpers. The Ruby helpers that resolve bundle paths still require Shakapacker configuration. Apps with Shakapacker config keep Shakapacker's guard unchanged. Fixes #3145. #3365 by justin808. -
[Pro] HTTP rolling-deploy bundle responses now include stronger no-cache headers: The built-in rolling-deploy manifest and bundle endpoints now send
Pragma: no-cacheandX-Content-Type-Options: nosniffalongsideCache-Control: no-store, reducing the risk of legacy caching or MIME-sniffing mishandling authenticated bundle payloads. #3439 by justin808. -
[Pro] OpenTelemetry shutdown timeout warning never logged:
shutdownProviderWithTimeoutin the Node Renderer's OpenTelemetry integration was missing alog.warn(call, leaving a bare string literal that produced no diagnostic whenprovider.shutdown()exceeded its timeout (and broke the source file's compilation). The timeout message now logs correctly. Follow-up to #3382. #3420 by justin808. -
[Pro] HTTP rolling-deploy adapter now streams bundle downloads with a compressed-size cap:
ReactOnRailsPro::RollingDeployAdapters::HttpwritesGET /bundles/:hashsuccess responses to a Tempfile with a 50 MB compressed-body limit before extraction, and drains non-success responses through the same cap so oversized error bodies cannot exhaust heap. The existing 200 MB uncompressed tarball cap remains in place. Fixes #3416. #3435 by justin808. -
bin/devand doctor now label live reload defaults correctly:bin/dev helpandreact_on_rails:doctornow readconfig/shakapacker.ymlbefore describing the defaultProcfile.devmode, so apps using Shakapacker's live reload default are labeled as live reload instead of HMR. Doctor also treats Shakapacker'shmr: onlymode as HMR when deciding whether to show HMR-specific warnings. Dev-server mode detection accepts both unquoted YAML booleans (hmr: true) and their quoted, case-insensitive equivalents (hmr: "true",hmr: "TRUE"), so apps with the quoted-boolean YAML anti-pattern still get correct labels and HMR-specific doctor warnings. Other non-boolean values (integers, arbitrary strings besides"only") and configs without an explicitdev_serverblock fall back to Shakapacker's live reload default for help text. Apps that keep HMR settings only under top-leveldefault.dev_servershould move them underdevelopment.dev_serveror inherit them with a YAML anchor if they want HMR-specific doctor warnings. CustomSHAKAPACKER_CONFIGpaths and symbol-valued YAML settings are respected during mode detection. Fixes #3374. #3377 by justin808. -
[Pro] Streaming server-render responses now raise
ReactOnRailsPro::Errorwhen the stream response status is unavailable or the renderer delivers a readable HTTP error status as a streaming body, instead of silently returning no chunks. This is a user-visible behavior change for callers that do not already rescueReactOnRailsPro::Errorfromeach_chunk. #3383. -
[Pro] TanStack Router hydration now supports the current router stores API:
react-on-rails-pro/tanstack-routerclient hydration now uses TanStack Router's currentrouter.stores.setMatches()API whenrouter.__store.setState()is unavailable, so SSR hydration works with newer@tanstack/react-routerreleases without app-level compatibility shims. Fixes #3375. #3376 by justin808. -
[Pro] TanStack Router hydration no longer double-calls
loadRouteChunkunder React 18 StrictMode: React 18's StrictMode double-renders components with fresh hook state on each pass, so therouterRef.current === nullguard inclientHydrate.tsfired twice whenoptions.createRouterreturned the same router instance, re-runningloadRouteChunk,__store.setState, and the user-definedhydratecallback. The render-phase init is now memoized via a module-levelWeakMapkeyed on the router instance, dedup'ing per-router side effects across mount cycles. Production behavior is unchanged because each mount creates a fresh router. Fixes #3405. #3410 by justin808. -
[Pro] Benchmark CI starts the production dummy app on the expected port:
react_on_rails_pro/spec/dummy/bin/prodnow setsPORT=3001by default before launching Foreman, preventing Foreman's defaultPORT=5000from making the Rails server miss the benchmark workflow readiness check onlocalhost:3001. Bothreact_on_rails/spec/dummy/bin/prodandreact_on_rails_pro/spec/dummy/bin/prodrespectPORTwhen it's set. #3403 by alexeyr-ci2. -
CI fails on stale lockfiles outside minimum-dependency jobs: GitHub Actions now runs Bundler with frozen lockfiles for standard integration, Pro, Playwright, lint, and precompile jobs, and no longer mutates lockfiles with
bundle lock --add-platform.pnpm installis also frozen in Playwright. The intentionally mutable minimum-dependency jobs still use non-frozen installs afterscript/convert. #3404, #3430 by alexeyr-ci2. -
[Pro] Generated
bin/devProcfiles now start the Node Renderer: Pro setup now appends anode-rendererprocess toProcfile.dev,Procfile.dev-static-assets, andProcfile.dev-prod-assetswhen those files exist, so SSR pages work inbin/dev,bin/dev static, andbin/dev prod.react_on_rails:doctornow warns when a Pro NodeRenderer app's launcher Procfiles can serve Rails pages but do not start a renderer onRENDERER_PORT. Fixes #3372. #3381 by justin808. -
Install generator preserves explicit version pins when package-manager install fails: The install generator's
add_packagespath now writes versionedname@versionspecs directly intopackage.json(underdependenciesordevDependencies) as a last-resort fallback when neither the primary nor fallback package manager install succeeds, so users can rerun their package manager manually without losing the pins. Specs without an explicit version are not written. #3387 by justin808. -
[Pro] RSC client manifest restored when only
registerServerComponent/clientis in the pack graph:wrapServerComponentRenderer/clientnow directly importsreact-on-rails-rsc/client.browseras a side-effect import. Previously the client runtime was only reachable through a three-level transitive chain (wrapServerComponentRenderer/client→getReactServerComponent.client→react-on-rails-rsc/client.browser). Tooling that severed any link in that chain (tree-shaking, transpiler quirks, customNormalModuleReplacement, externals) causedRSCWebpackPluginto emitClient runtime at react-on-rails-rsc/client was not found. React Server Components module map file react-client-manifest.json was not created.and silently skip the manifest, breaking RSC hydration on the Pro Node Renderer. The direct import keeps the runtime resource in the module graph so the plugin always emitsreact-client-manifest.json. Fixes #3366. #3368 by justin808. -
[Pro] Updated Fastify in the Node Renderer for CVE-2026-33806: Raised the direct
fastifydependency to 5.8.5 so user-provided Fastify server options, includingtrustProxy, pick up the upstream security fix. #3152 by dependabot[bot]. -
[Pro] TanStack Router hydration no longer bails to a full client re-render: TanStack Router SSR pages no longer discard server-rendered HTML during hydration because the client tree now renders
RouterProviderwith the same shape as the server output. Post-hydration navigation still waits for matched lazy route chunks beforerouter.load(). #3213 by Seifeldin7. -
[Pro] Widened ruby-jwt support to
jwt >= 2.7: React on Rails Pro relaxes the previous~> 2.7cap tojwt >= 2.7, so applications can resolve the patched ruby-jwt 3.2.0+ release for the empty-key HMAC advisory while apps still on jwt 2.x remain compatible. #3322, #3344 by ihabadham. -
[Pro] Pro migration generator rewrites all base-package references and preserves Gemfile pins:
rails generate react_on_rails:pronow rewrites Jest/Vitest mock helpers (jest.mock,vi.mock,requireActual/importActual, and the rest) and TypeScriptdeclare module 'react-on-rails'blocks alongside its existingimport/require/dynamic-import handling, and the Gemfile swap now preserves the user's existing version pin (and other gem options) instead of overwriting them with the running gem's version.react_on_rails:doctoris widened to match: it also flags stale side-effect imports (import 'react-on-rails';), Jest/Vitest mock helpers, anddeclare moduleblocks, and the new side-effect-import pattern keeps the doctor a superset of the rewriter so anything the rewriter doesn't reach gets surfaced. Closes #3104. #3232 by justin808. -
[Pro] Pro migration scans TypeScript 4.7
.mtsand.ctsmodules:react_on_rails:doctorand the Pro migration rewriter now include.mts/.ctssource files (and their.d.mts/.d.ctsdeclaration counterparts) when looking for stalereact-on-railsreferences, matching the existing.mjs/.cjscoverage. Fixes #3250. #3334 by justin808. -
Doctor now honors nested JavaScript package roots:
react_on_rails:doctornow checks package-manager lockfiles,package.json, and installed React from the configurednode_modules_location, reducing false diagnostics for legacy apps that keep dependencies underclient/. The Vite migration guide now documents the supported thin-wrapper pattern for those layouts. Note: a missingpackage.jsonat the configurednode_modules_locationnow emits a warning instead of being silently skipped, so apps misconfigured against a nonexistent path will see new diagnostics on upgrade. Fixes #3205. #3220 by justin808. -
Generated pack regeneration is now serialized:
generate_packs_if_stalenow uses a Railstmp/lock file, re-checks staleness after waiting, and avoids concurrent cleanup/regeneration races when multiple processes trigger auto-bundling at the same time. Fixes #1627. #3231 by justin808. -
Install generator validates the selected JavaScript package manager: The install generator now checks the manager selected from
REACT_ON_RAILS_PACKAGE_MANAGER, thepackageManagerfield inpackage.json, or a lockfile on disk — instead of passing when any JavaScript package manager is installed. When the selected command is missing, the error names the selected manager, the source that selected it, and the available alternatives. The generator also warns whenREACT_ON_RAILS_PACKAGE_MANAGERis set to a value outside the supported set (npm,pnpm,yarn,bun). Addresses package manager validation from #1958. #3229 by justin808. -
Server-render error wrapping preserves original causes: When server rendering catches a non-
Errorthrown value, React on Rails now wraps it with the original value attached ascause, making downstream debugging preserve more context. Fixes #1746. #3230 by justin808. -
bin/devnow cleans copied runtime files before startup: When you duplicate an app directory to run another local dev stack,bin/devnow removes copied stale Overmind sockets and staletmp/pids/server.pidfiles that point to a Puma process running from another app directory. This prevents false startup failures in copied workspaces while still preserving active local sockets and pid files for the current app. #3142 by justin808. -
bin/dev killis more thorough and Pro-aware under base-port mode:ServerManager.kill_processesno longer short-circuits after the first successful step — pattern-based kills, port-based kills, and socket/pid cleanup all run unconditionally so a stale renderer port-binding or socket file cannot survive abin/dev kill. In base-port mode, the derived renderer port (base+2) is now always included in port-based killing whenreact_on_rails_prois loaded, even ifRENDERER_PORT/REACT_RENDERER_URLare unset in the current shell (an informational message is printed so the wider scan is not silent).ProcessManageralso now preserves the legacyRENDERER_URLenv var alongsideREACT_RENDERER_URLacross Bundler's env reset so mid-migration users keep a consistent renderer URL in spawned subprocesses. #3274 by justin808. -
[Pro] RSC setup now scopes client-reference discovery to app source: Generated RSC webpack configs now pass
clientReferencesbased on Shakapacker'ssource_path, avoiding CI failures where the plugin could scan vendored gem templates undervendor/bundle. Fixes #3201. #3219 by justin808. -
[Pro] Node renderer now exposes
performancewhensupportModules: true: React 19's development build ofReact.lazycallsperformance.now(), which previously threwReferenceError: performance is not definedinside the node renderer's VM context unless users manually addedperformanceviaadditionalContext.performanceis now included in the default globals alongsideBuffer,process, etc. Fixes #3154. #3158 by justin808. -
Scaffolded CI workflow pins a pnpm version when
packageManageris absent: The generated.github/workflows/ci.ymlnow emitswith: version:forpnpm/action-setup@v4when pnpm is detected frompnpm-lock.yamlalone, preventing the setup step from failing before dependency install. WhenpackageManageris declared inpackage.json, the version key is omitted so the action reads the pin from there. Note:GeneratorMessages.detect_package_manager(package_json: nil)now treatsnilas "caller cached that the file is absent" and skips disk fallback, instead of re-readingpackage.json; the previous fallthrough behavior is now the default (omit the keyword) and is documented onread_package_json. Fixes #3172. #3174 by justin808. -
Client startup now recovers if initialization begins during
interactiveafterDOMContentLoadedalready fired: React on Rails now still initializes the page when the client bundle starts in the browser timing window afterDOMContentLoadedbut before the document reachescomplete. Fixes #3150. #3151 by ihabadham. -
Doctor accepts TypeScript server bundle entrypoints:
react_on_rails:doctornow resolves common source entrypoint suffixes (.js,.jsx,.ts,.tsx,.mjs,.cjs) before warning that the server bundle is missing, preventing false positives when apps useserver-bundle.ts. #3111 by justin808. -
Doctor no longer fails custom projects for a missing generated
bin/dev:react_on_rails:doctornow downgrades a missing official React on Railsbin/devlauncher from an error to a warning and adds explicit guidance when a custom./devscript is detected, so custom projects can pass diagnostics when their development setup is intentional. Fixes #3103. #3117 by justin808. -
[Pro] Reduced
react-on-rails-pro-node-rendererpublished package size: added afileswhitelist topackage.jsonsopnpm packno longer includessrc/,tests/fixtures,*.map, andlib/tsconfig.tsbuildinfo— matching the convention used by the sibling packages. Also markedreact_on_rails_pro/spec/dummyasprivateso it can never be accidentally published. #3304 by alexeyr-ci2. -
[Pro] HTTPX bidirectional streaming reliability: Fixed streaming request timeouts when using HTTPX with both the
:streamand:stream_bidiplugins. The request now uses thebuild_requestpattern with an explicitrequest.closeso the HTTP/2END_STREAMflag is sent, and a temporary monkey-patch (httpx_stream_bidi_patch.rb) works around an upstream:stream_bidiretry bug that left stale body callbacks registered and crashed retried requests withprotocol_error. The patch is scoped and will be removed once fixed upstream. #2903 by AbanoubGhadban. -
[Pro] Progressive RSC streaming flush granularity: RSC streaming now flushes on React's per-render-cycle
flush()signal instead ofsetTimeout(flush, 0), so the shell and each resolved<Suspense>boundary stream as separate chunks rather than being merged into one large first message. This restores progressive streaming (and fixes worse-than-SSR First Contentful Paint) on pages with fast queries, and eliminates partial-HTML-tag chunks. Fixes #3194. #2903 by AbanoubGhadban. -
[Pro] Node renderer graceful shutdown after stream timeouts: Fixed workers taking 30+ seconds to shut down after a
StreamChunkTimeoutErrorduring streaming.handleGracefulShutdownnow also decrements the active-request count ononRequestAbort/onTimeout, thePassThroughwrapper is destroyed when the source render stream errors, and the HTTP response is closed on chunk timeout so connections to Rails no longer hang. Fixes #2270 and #2308. #2903 by AbanoubGhadban.
Deprecated
- [Pro]
config.renderer_http_keep_alive_timeoutis deprecated: The setting now has no effect because async-http manages renderer client lifecycle through scheduler-scoped clients when aFiber.scheduleris already running and per-request clients otherwise. Explicitly setting it to a non-nilvalue emits a deprecation warning; leaving it unset or setting it tonilis accepted silently. Remove non-nilconfiguration during upgrade. Seedocs/pro/updating.mdfor the full upgrade guide. #3320 by AbanoubGhadban.
Removed
- [Pro] Removed the legacy license key-file migration warning: The elapsed migration notices for the legacy
config/react_on_rails_pro_license.keyfile path (the cleanup notice shown alongside a valid configured license, and the missing-license migration notice) are no longer emitted. The legacy file itself was already unread. Fixes #3624. #3715 by justin808. - [Pro] Removed HTTPX transport gem dependencies from the Node Renderer: React on Rails Pro no longer depends on
httpx,http-2, orconnection_poolafter migrating toasync-http. Applications that directly pin or require those gems for renderer integration should remove that coupling or add their own explicit dependency. #3320 by AbanoubGhadban. - [Pro] Removed the
--rsc-proinstall generator flag:--rscalready implies Pro, so the separate mode was unnecessary. Behaviors previously gated on--rsc-pro(Pro verification checklist, prerelease install note, exact Pro gem pin on prereleases) now fire on--rscinstalls. See also #3104, which tracks unrelated silent-failure bugs in the Pro upgrade automation. #3105 by ihabadham.
Security
- [Pro] Hardened Node Renderer password lifecycle: The protocol-mismatch (412) response no longer echoes the request body verbatim — only non-sensitive field names are returned, so a mismatched
passwordis never reflected back. The Pro install generator now provisions a random 64-hex-character renderer password instead of the publicly-knowndevPassworddefault, and production startup now logs a non-blocking warning for known-weak or short (< 16 character) renderer passwords. Fixes #3397. #3399 by AbanoubGhadban.