Breaking Changes
- [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. PR 3320 by AbanoubGhadban. - [Pro] Async Rails server deployments need to stay on the HTTPX renderer until support is added: Falcon and async-rails deployments are not currently supported with the new async-http renderer client because calling the renderer from inside an existing Async reactor without an
Async::Task.current?context can create a nested reactor. Keep those deployments on the previous HTTPX renderer client until support is explicitly added. Seedocs/pro/updating.mdfor the full upgrade guide. PR 3320 by AbanoubGhadban. - [Pro]
config.renderer_http_pool_sizenow limits per-request HTTP/2 streams: Existing numeric values now cap concurrent HTTP/2 streams for each request-scoped renderer client instead of sizing a persistent process-wide connection pool. Setting a non-default value emits a warning so the changed meaning is visible during upgrades; settingnilkeeps the default stream limit and does not make the request-scoped client unlimited. Persistent connection reuse is tracked in Issue 3283. Seedocs/pro/updating.mdfor the full upgrade guide. PR 3320 by AbanoubGhadban.
Added
- [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 Issue 3324. PR 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 Issue 3419. PR 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. PR 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 Issue 2156. PR 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 Issue 3390. PR 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 Issue 3137. PR 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. PR 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. PR 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. PR 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). PR 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. PR 2903 by AbanoubGhadban.
Changed
-
[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_. PR 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. PR 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. PR 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. PR 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. PR 3401 by alexeyr-ci2. -
[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. PR 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 PR 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. PR 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. PR 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. PR 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 Issue 3073. PR 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. PR 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. PR 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 Issue 3247. PR 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 Issue 3082. PR 3084 by justin808.
Improved
- 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. PR 3444 by justin808.
Fixed
- [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 Issue 3184. PR 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 Issue 3455. PR 3462 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 Issue 3182. PR 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 Issue 3269. PR 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 Issue 3145. PR 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. PR 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 PR 3382. PR 3420 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. PR 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 Issue 3375. PR 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 Issue 3405. PR 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. PR 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. PR 3404, PR 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 Issue 3372. PR 3381 by justin808. - Prerelease changelog auto-versioning now warns for cross-channel reuse:
bundle exec rake update_changelog[rc]and[beta]now correctly warn when an active prerelease base exists only in a different prerelease channel, helping maintainers catch accidental channel switches before stamping a release header. PR 3417 by justin808. - Release pipeline verifies published npm packages and blocks
workspace:dependency leaks:rake release:npmnow pollsnpm viewafter eachpnpm publishand aborts when the published version is missing, mismatched, or when any install-timedependencies/optionalDependencies/peerDependenciesstill contains aworkspace:protocol entry. Before publishing, package manifests are temporarily rewritten to replaceworkspace:ranges with publishable semver and restored afterward. The broken16.7.0-rc.1npm publish shippedreact-on-rails-pro@16.7.0-rc.1withreact-on-rails: "workspace:*"in its dependency metadata, which Yarn v1 cannot install from the registry; this safeguard prevents future releases from leaking the same protocol. PR 3387 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. PR 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. PR 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. PR 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(). PR 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. PR 3322, PR 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 Issue 3104. PR 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 Issue 3250. PR 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 Issue 3205. PR 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 Issue 1627. PR 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 Issue 1958. PR 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 Issue 1746. PR 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. PR 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. PR 3274 by justin808.- CI change detection handles shallow clones with long-lived branches:
script/ci-changes-detectorandscript/check-docs-sidebarnow resolve an actual merge base before diffing, deepening shalloworigin/mainand current-branch history as needed.ci-changes-detectornow fails visibly when it cannot compute a safe diff instead of treating git failures as no changes. Fixes Issue 3108. PR 3224 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 Issue 3201. PR 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 Issue 3154. PR 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 Issue 3172. PR 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 Issue 3150. PR 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. PR 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 Issue 3103. PR 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. PR 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. PR 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 Issue 3194. PR 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 Issue 2270 and Issue 2308. PR 2903 by AbanoubGhadban.
Deprecated
- [Pro]
config.renderer_http_keep_alive_timeoutis deprecated: The setting now has no effect because async-http renderer clients are scoped to individual requests. Setting it emits a deprecation warning; remove the configuration during upgrade. Seedocs/pro/updating.mdfor the full upgrade guide. PR 3320 by AbanoubGhadban.
Removed
- [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. PR 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 Issue 3104, which tracks unrelated silent-failure bugs in the Pro upgrade automation. PR 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 Issue 3397. PR 3399 by AbanoubGhadban.