github shakacode/react_on_rails v16.7.0.rc.0

pre-release2 hours ago

Added

  • bin/dev deterministic port allocation via REACT_ON_RAILS_BASE_PORT (and CONDUCTOR_PORT): bin/dev now derives Rails / webpack-dev-server / node-renderer ports from a single base port when REACT_ON_RAILS_BASE_PORT (or CONDUCTOR_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-set PORT, SHAKAPACKER_DEV_SERVER_PORT, RENDERER_PORT, or non-matching REACT_RENDERER_URL (and the legacy RENDERER_URL, when already set) is unconditionally overwritten with the derived value (a warning is printed before each override). This applies in all bin/dev modes including bin/dev prod, where SHAKAPACKER_DEV_SERVER_PORT is 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/dev now also preserves RENDERER_PORT, REACT_RENDERER_URL, and SHAKAPACKER_SKIP_PRECOMPILE_HOOK across Bundler's env reset (previously only PORT and SHAKAPACKER_DEV_SERVER_PORT were preserved); this prevents nested shakapacker commands from silently re-running the precompile hook or losing the renderer URL. PR 3142 by justin808.
  • [Pro] bin/dev auto-derives REACT_RENDERER_URL from RENDERER_PORT: When only RENDERER_PORT is set, bin/dev now sets REACT_RENDERER_URL=http://localhost:RENDERER_PORT so Rails reaches the right port by default. Users running a remote or non-localhost node renderer (Docker service, remote host) should set REACT_RENDERER_URL explicitly 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_cache rake 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. Supports RENDERER_SERVER_BUNDLE_CACHE_PATH, RSC bundles, and rolling-deploy guidance centered on current and previous bundle hashes. The legacy pre_stage_bundle_for_node_renderer task now stages the same cache layout via symlinks for same-filesystem workflows. Note: RENDERER_BUNDLE_PATH is now deprecated in favor of RENDERER_SERVER_BUNDLE_CACHE_PATH across both tasks. Existing users with RENDERER_BUNDLE_PATH set will see a deprecation warning on stderr. PR 3124 by justin808.
  • [Pro] Rolling-deploy adapter protocol: New config.rolling_deploy_adapter pluggable 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:precompile auto-calls upload in production-like environments so the next deploy can fetch the just-built bundle. PREVIOUS_BUNDLE_HASHES env var overrides discovery for CI. react_on_rails:doctor probes the adapter and reports protocol conformance, discovery latency, and resolved cache dir. Each seeded hash carries its own loadable-stats.json / RSC manifests so client-side hydration stays consistent with the deployed asset pipeline for that hash. See docs/pro/rolling-deploy-adapters.md for 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_props and rsc_payload_react_component_with_async_props view 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 the getReactOnRailsAsyncProp function injected into props (typed via the new WithAsyncProps TypeScript helper). Requires config.enable_rsc_support = true. This is an additive feature — existing stream_react_component and rsc_payload_react_component calls are unaffected. PR 2903 by AbanoubGhadban.

Changed

  • [Pro] PreSeedRendererCache and PrepareNodeRenderBundles now auto-stage loadable-stats.json: ReactOnRailsPro::RendererCacheHelpers.collect_assets now appends loadable-stats.json whenever 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 your assets_to_copy config explicitly listed loadable-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_assets keeps 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>.js layout. The react_on_rails_pro:pre_seed_renderer_cache rake task accepts MODE=copy (default; Docker/image builds) or MODE=symlink (same-filesystem). The auto-invocation at the end of assets:precompile defaults to :symlink (preserving prior behavior) and now honors ASSETS_PRECOMPILE_RENDERER_CACHE_MODE=copy|symlink so Docker builds that run rake assets:precompile as the final asset step can opt into copy mode without invoking the rake task separately. MODE=copy raises a clear error when neither RENDERER_SERVER_BUNDLE_CACHE_PATH nor RENDERER_BUNDLE_PATH is 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 legacy react_on_rails_pro:pre_stage_bundle_for_node_renderer task and ReactOnRailsPro::PrepareNodeRenderBundles class remain as deprecated shims that emit a once-per-process warning and delegate to mode: :symlink. react_on_rails:doctor flags 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>.js instead. 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-level renderer/ directory instead of client/, making it straightforward to exclude from production Docker builds that strip JS sources after bundling. Docs and Pro spec/dummy now use the new path consistently. Existing apps are unaffected — the generator skips files that already exist (including a legacy client/node-renderer.js). Fixes Issue 3073. PR 3165 by justin808.
  • [Pro] Documentation standardized on REACT_RENDERER_URL env var name: The configuration example in docs/oss/configuration/configuration-pro.md now shows ENV["REACT_RENDERER_URL"] instead of the older ENV["RENDERER_URL"], aligning with the rest of the docs and the generator template. Existing apps that read ENV["RENDERER_URL"] in their initializer continue to work — the Pro renderer_url config 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/dev now also warns when RENDERER_URL is set without REACT_RENDERER_URL so 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 the react_on_rails/react_on_rails_pro gems, the react-on-rails/react-on-rails-pro npm packages, and the react-on-rails-pro-node-renderer as 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:doctor renderer-cache scan covers CI/CD manifests: The deprecated-task scan that flags react_on_rails_pro:pre_stage_bundle_for_node_renderer now also checks .circleci/config.yml, .gitlab-ci.yml, bitbucket-pipelines.yml, every .github/workflows/*.yml/.yaml, and every config/deploy/*.rb stage file, on top of the existing Procfile/Dockerfile/Compose/Kamal/Capistrano/bin/*/scripts/deploy.sh paths. 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.
  • Rspack install scaffolding now targets Rspack v2: react_on_rails:install --rspack and bin/switch-bundler now 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 keeping rspack-manifest-plugin@^5.0.0, which is already compatible. Closes Issue 3082. PR 3084 by justin808.

Fixed

  • [Pro] Allow patched ruby-jwt releases: React on Rails Pro now requires jwt >= 3.2.0, removing the previous ~> 2.7 cap so applications can resolve the patched ruby-jwt release for the empty-key HMAC advisory. PR 3322 by ihabadham.
  • [Pro] Pro migration generator rewrites all base-package references and preserves Gemfile pins: rails generate react_on_rails:pro now rewrites Jest/Vitest mock helpers (jest.mock, vi.mock, requireActual/importActual, and the rest) and TypeScript declare module 'react-on-rails' blocks alongside its existing import/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:doctor is widened to match: it also flags stale side-effect imports (import 'react-on-rails';), Jest/Vitest mock helpers, and declare module blocks, 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.
  • Doctor now honors nested JavaScript package roots: react_on_rails:doctor now checks package-manager lockfiles, package.json, and installed React from the configured node_modules_location, reducing false diagnostics for legacy apps that keep dependencies under client/. The Vite migration guide now documents the supported thin-wrapper pattern for those layouts. Note: a missing package.json at the configured node_modules_location now 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_stale now uses a Rails tmp/ 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, the packageManager field in package.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 when REACT_ON_RAILS_PACKAGE_MANAGER is 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-Error thrown value, React on Rails now wraps it with the original value attached as cause, making downstream debugging preserve more context. Fixes Issue 1746. PR 3230 by justin808.
  • bin/dev now cleans copied runtime files before startup: When you duplicate an app directory to run another local dev stack, bin/dev now removes copied stale Overmind sockets and stale tmp/pids/server.pid files 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 kill is more thorough and Pro-aware under base-port mode: ServerManager.kill_processes no 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 a bin/dev kill. In base-port mode, the derived renderer port (base+2) is now always included in port-based killing when react_on_rails_pro is loaded, even if RENDERER_PORT / REACT_RENDERER_URL are unset in the current shell (an informational message is printed so the wider scan is not silent). ProcessManager also now preserves the legacy RENDERER_URL env var alongside REACT_RENDERER_URL across 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-detector and script/check-docs-sidebar now resolve an actual merge base before diffing, deepening shallow origin/main and current-branch history as needed. ci-changes-detector now 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] Node renderer now exposes performance when supportModules: true: React 19's development build of React.lazy calls performance.now(), which previously threw ReferenceError: performance is not defined inside the node renderer's VM context unless users manually added performance via additionalContext. performance is now included in the default globals alongside Buffer, process, etc. Fixes Issue 3154. PR 3158 by justin808.
  • Scaffolded CI workflow pins a pnpm version when packageManager is absent: The generated .github/workflows/ci.yml now emits with: version: for pnpm/action-setup@v4 when pnpm is detected from pnpm-lock.yaml alone, preventing the setup step from failing before dependency install. When packageManager is declared in package.json, the version key is omitted so the action reads the pin from there. Note: GeneratorMessages.detect_package_manager(package_json: nil) now treats nil as "caller cached that the file is absent" and skips disk fallback, instead of re-reading package.json; the previous fallthrough behavior is now the default (omit the keyword) and is documented on read_package_json. Fixes Issue 3172. PR 3174 by justin808.
  • Client startup now recovers if initialization begins during interactive after DOMContentLoaded already fired: React on Rails now still initializes the page when the client bundle starts in the browser timing window after DOMContentLoaded but before the document reaches complete. Fixes Issue 3150. PR 3151 by ihabadham.
  • Doctor accepts TypeScript server bundle entrypoints: react_on_rails:doctor now resolves common source entrypoint suffixes (.js, .jsx, .ts, .tsx, .mjs, .cjs) before warning that the server bundle is missing, preventing false positives when apps use server-bundle.ts. PR 3111 by justin808.
  • Doctor no longer fails custom projects for a missing generated bin/dev: react_on_rails:doctor now downgrades a missing official React on Rails bin/dev launcher from an error to a warning and adds explicit guidance when a custom ./dev script 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-renderer published package size: added a files whitelist to package.json so pnpm pack no longer includes src/, tests/ fixtures, *.map, and lib/tsconfig.tsbuildinfo — matching the convention used by the sibling packages. Also marked react_on_rails_pro/spec/dummy as private so 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 :stream and :stream_bidi plugins. The request now uses the build_request pattern with an explicit request.close so the HTTP/2 END_STREAM flag is sent, and a temporary monkey-patch (httpx_stream_bidi_patch.rb) works around an upstream :stream_bidi retry bug that left stale body callbacks registered and crashed retried requests with protocol_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 of setTimeout(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 StreamChunkTimeoutError during streaming. handleGracefulShutdown now also decrements the active-request count on onRequestAbort/onTimeout, the PassThrough wrapper 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.

Removed

  • [Pro] Removed the --rsc-pro install generator flag: --rsc already 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 --rsc installs. See also Issue 3104, which tracks unrelated silent-failure bugs in the Pro upgrade automation. PR 3105 by ihabadham.

Don't miss a new react_on_rails release

NewReleases is sending notifications on new releases.