github shakacode/react_on_rails v17.2.0

6 hours ago

Changed

  • [Pro] React 19.3 support now requires stable react-on-rails-rsc 19.3.1: The Pro packages build and
    test against stable react-on-rails-rsc 19.3.1 (npm latest) with React/React DOM 19.3.0, replacing the
    19.3.1-rc.0 release candidate that 17.2.0.rc.0 and 17.2.0.rc.1 used. The react-on-rails-pro optional peer
    range is >=19.2.1 <20.0.0 again; the temporary ~19.3.1-rc.0 alternative is removed. The node renderer
    startup check and react_on_rails:doctor accept only stable react-on-rails-rsc releases: 19.2.x with
    React 19.2.7+, 19.3.0 with React 19.2.8+, and 19.3.x from 19.3.1 with React 19.3.x. Action required for upgraders:
    apps that installed react-on-rails-rsc 19.3.1-rc.0 or 19.3.1-rc.1 while testing a 17.2.0 release candidate
    must install stable 19.3.1 (React/React DOM stay on 19.3.0). The node renderer refuses those prereleases at
    startup, and both its error and Doctor's Fix name 19.3.1. Apps on stable 19.2.x or 19.3.0 need no change.
    #5162 by
    justin808.

  • [Pro] New RSC apps install the React 19.3 line: rails g react_on_rails:install --rsc,
    rails g react_on_rails:rsc, and create-react-on-rails-app --rsc now install react-on-rails-rsc 19.3.1 with
    react@~19.3.0 and react-dom@~19.3.0. The 17.0 generator installed react-on-rails-rsc 19.2.1 with
    React 19.2.7, and apps on that set keep working unchanged. Apps generated by 17.1.0 pin the superseded
    19.3.0-rc.4, which the node renderer refuses at startup: install react-on-rails-rsc 19.3.0 with React/React DOM
    19.2.8+, or 19.3.1 with React/React DOM 19.3.0. Running
    rails g react_on_rails:rsc on an existing Pro app that is still on React 19.2.x now warns that the installed
    react-on-rails-rsc 19.3.1 needs React 19.3.x; upgrade React and React DOM to ~19.3.0, or keep React 19.2.x
    and pin react-on-rails-rsc@19.2.1 yourself. When Doctor has no closer match, its Fix now recommends the same
    19.3.1 / React 19.3.0 set the generator installs.
    #5164 by
    justin808.

  • [Pro] License: React on Rails Pro moves to The React on Rails Pro License 3.0, an application of ShakaCode
    Trust-Based Commercial Licensing that matches ShakaPerf: free in production for small organizations, charities,
    educational institutions, and hospitals; 45-day production evaluation; 30-day grace after a subscription lapses;
    the license key stays optional and only sets the attribution status.
    Published gem metadata now uses LicenseRef-ReactOnRailsPro, and generator messages reflect these terms.
    #5104 by
    sashakhar1.

Fixed

  • [Pro] Doctor no longer reports a superseded prerelease as newer: react_on_rails:doctor warned that
    an installed stable react-on-rails-rsc was behind the npm next dist-tag even when next pointed at a
    release candidate that stable latest had already replaced (for example next 19.3.1-rc.1 with latest
    19.3.1), a prerelease this version rejects. Doctor now skips a next or rc tag that is not newer than
    latest.
    #5164 by
    justin808.

  • [Pro] Rejected react-on-rails-rsc prereleases now say why: When the node renderer startup check or
    react_on_rails:doctor rejects a prerelease such as 19.3.2-rc.0, the message now reads
    >= 19.2.1 (stable releases only). The previous >= 19.2.1 label was satisfied by the prerelease it rejected.
    #5168 by
    justin808.

  • [Pro] Streamed useId hydration: Client components now hydrate with the server's useId prefix even when
    they are not wrapped by the default RSC provider. This prevents hydration mismatches and keeps
    generated IDs consistent with accessible references. Fixes #5146.
    #5148 by justin808.

  • [Pro] Node Renderer no longer drops multipart files that follow a large file over HTTP/1.1: With
    fastifyServerOptions: { http2: false }, file parts uploaded after a large file part were silently discarded
    while the request still returned 200. With RSC enabled, a large assets_to_copy file such as
    loadable-stats.json caused the RSC manifests behind it to go missing, so the first RSC render failed with
    ENOENT ... react-client-manifest.json. The renderer now keeps every file part, and requests over the
    aggregate upload limit still return 413. HTTP/2 (the default transport) was not affected. Fixes
    #5144.
    #5147 by justin808.

  • Published JavaScript no longer ships dangling sourceMappingURL pointers: Every published lib/**/*.js file
    in react-on-rails and react-on-rails-pro ended with a //# sourceMappingURL=<name>.js.map comment, but the
    packages never published the .map files, so each pointer referenced a missing file. Besides devtools warnings,
    the dangling pointers crashed RSC bundle builds (SyntaxError: ... is not valid JSON from
    react-on-rails-rsc/WebpackLoader) whenever server-component code imported one of the Pro package's own
    'use client' components such as react-on-rails-pro/RSCRoute. The packages' tsc builds no longer emit
    JavaScript sourcemaps, removing the pointer comments without changing the emitted JavaScript. (TypeScript
    declarationMap pointers in published .d.ts files are unaffected — cosmetic, tracked as a follow-up.) Part of
    #5079.
    #5099 by
    AbanoubGhadban.

  • [Pro] RSC client-reference resolution now registers the Pro package's own 'use client' components:
    Client-reference discovery only scans app source directories (node_modules is excluded), so the 'use client'
    components the react-on-rails-pro npm package itself ships — react-on-rails-pro/RSCRoute,
    react-on-rails-pro/RSCProvider, and react-on-rails-pro/registerDefaultRSCProvider/client — could never appear
    in react-client-manifest.json. A server component rendering one of them (for example a nested <RSCRoute> used
    for section-level refetching) then failed the render-time manifest lookup even when the RSC bundle compiled. The
    client-reference resolver emitted by the react_on_rails:rsc generator now appends these components as explicit
    clientReferences entries (via require.resolve) to every resolution branch, on both the client and server
    manifest plugins. Action required for existing RSC apps: a resolver emitted by a previous generator version is
    never rewritten — re-running rails g react_on_rails:rsc warns and leaves it unchanged. Add the require.resolve
    entries for the three components to your clientReferences manually, or remove the generated resolver block and
    re-run the generator (see the
    RSC setup docs). Part of
    #5079.
    #5100 by
    AbanoubGhadban.

  • [Pro] RSC streams no longer encode consumer logs into the Flight payload: React 19.3 development
    Flight keeps a console hook active while it flushes chunks, so logs from code consuming the returned RSC
    Readable were serialized as :W["log"...] rows. In non-production builds, RSC payload streams now use
    Node's native console while chunks reach the consumer. Production builds and HTML streams keep the
    caller's console unchanged. #5094 by
    justin808.

  • authenticityHeaders() no longer mutates its input object: The helper now returns a new merged object instead of writing CSRF headers into the caller's otherHeaders argument. Previously, passing a shared or module-level headers object would bake a stale CSRF token into it, causing intermittent 422 InvalidAuthenticityToken errors after Turbo navigations. Fixes #5028.

  • Version checking now resolves the installed package version from pnpm, bun, and Yarn Berry lockfiles:
    pnpm-lock.yaml (lockfileVersion 5.4/6.0/9.0, including the pnpm 11 multi-document form), bun.lock
    (lockfileVersion 0-2, JSONC), and Yarn Berry yarn.lock (__metadata versions 4-8) join the existing Yarn
    classic and package-lock.json support, with the same package.json fallback and precedence behavior as before —
    so semver ranges like ^17.0.0 in package.json no longer fail Rails boot for pnpm, bun, and Yarn 2+ users.
    The binary bun.lockb is not parsed — migrate with
    bun install --save-text-lockfile --frozen-lockfile --lockfile-only, or pin the exact version. A malformed or
    unreadable lockfile falls back to the package.json version instead of erroring, and the non-exact-version error
    text now explains the relaxed installed-version rule instead of forbidding semver ranges outright. Fixes
    #5049.
    #5058 by
    AbanoubGhadban.

  • bin/dev kill now verifies every app-scoped Overmind endpoint within a bounded control budget:
    shutdown discovers all tmp/sockets/overmind*.sock endpoints, fails closed when discovery or probing cannot be
    completed, and terminates and reaps timed-out control clients under one shared deadline per phase. When no renderer
    port is exported, recognized generated Procfiles supply the local Node Renderer fallback without widening the kill
    scope to an unrelated listener on port 3800. Fixes
    #4944. #4986 by
    justin808.

  • Ruby type signatures now match supported helper inputs: RBS accepts the compatibility
    immediate_hydration: option on Controller#redux_store, Pathname locale directories,
    Hash prerender props, and asset-checker/compiler injection keywords. Package-manager detection
    declares its actual symbol results, while invalid inputs remain rejected by runtime type checks.
    Fixes #5037.

  • Configuration RBS signatures match supported values: Runtime type checking now accepts the documented
    Pathname directories, unset configuration values, and the check_database_on_dev_start keyword without changing
    runtime behavior. Fixes #5036
    by justin808.

  • [Pro] TieredCacheHandler keeps serving L1 hits for entries older than l1MaxTtlSeconds: Promoting an
    L2 hit into L1 rewrote the entry's revalidate but kept its original timestamp, so any entry older than
    l1MaxTtlSeconds was written to L1 already expired — permanently bypassing L1 for that key and paying an L2
    (e.g. Redis) round trip on every request. Promoted entries now expire at the earlier of the original entry's
    expiry and l1MaxTtlSeconds from promotion time, and entries with no remaining lifetime skip the L1 write.
    A non-positive l1MaxTtlSeconds now disables L1 entirely instead of storing permanent L1 entries.
    Action required for upgraders: if you run a persistent L1 (e.g. RedisCacheHandler) with a
    non-positive l1MaxTtlSeconds, the disabled L1 retains entries written before it was disabled —
    flush that L1 store before re-enabling it with a positive cap, or retained stale/indefinite entries
    become readable again.
    With the default in-memory L1 this was purely a performance bug; a RedisCacheHandler L1 could
    additionally serve entries past their intended expiry, because Redis applies TTLs at write time —
    promoted entries now always encode exactly the remaining lifetime. Fixes
    #5027.
    #5029 by
    AbanoubGhadban.

  • Console replay can no longer swallow the rest of the page, and replayed messages are no longer altered:
    A server-side console.log argument containing <!-- could switch the browser's HTML parser into a state where
    the console-replay <script> consumed the remainder of the document. The replay code is now escaped with the same
    lossless two-part escape used by Pro's RSC payload injection (<!-- → <\!--, </script → </\script), shared
    as one helper across core and Pro, so the browser console now receives the original logged text (previously
    </script was rewritten to (/script). Fixes
    #5034.
    #5035 by
    AbanoubGhadban.

  • [Pro] Standalone upgrades preserve customized bundler configurations: The Pro generator automatically
    upgrades only complete, unchanged configuration pairs from supported current templates. Customized, historical,
    missing, or ambiguous pairs remain unchanged with manual migration instructions, preventing helper redeclarations
    and partial upgrades. Fixes #4789.
    #5010 by justin808.

  • RSC agent guardrail upgrades now preserve complete files and clean up their stale hook groups:
    The installer atomically replaces copied skill and hook files while preserving their existing permission behavior,
    and removes PostToolUse groups only when removing a managed guardrail hook leaves the group empty. Unrelated
    Claude settings and hook groups remain unchanged. Addresses items 1 and 2 of
    #4815.

  • Explicit id: nil now uses the configured automatic DOM id behavior: Component rendering no longer omits the
    container id while still reporting that a random id is active. Fixes
    #4993.
    #4998 by
    justin808.

  • bin/dev kill now stops only the current app directory's processes, and verifies they are
    gone
    : the kill path matched command lines machine-wide (pgrep -f rails, pgrep -f overmind,
    pgrep -f ruby.*puma, and friends) and scanned ports with an unfiltered lsof -ti :PORT, so
    running it in one worktree could terminate a Rails server, a Webpack dev server, or an unrelated
    client connection belonging to a different checkout — then printed "All processes terminated"
    without checking that anything had actually stopped. bin/dev now claims a worktree-scoped,
    flock-backed session file (tmp/react_on_rails/dev-session.json) recording the app root it
    belongs to, the process group it leads, and the Overmind endpoint it would use. Session
    publication and kill-reader ownership are serialized through a fixed
    tmp/react_on_rails/dev-session.lock: writers publish a same-directory temporary file with an
    atomic rename while retaining the JSON file's lifetime lock, and a kill reader retains both locks
    through shutdown cleanup so another reader cannot authenticate a stale or recycled process group.
    A concurrent bin/dev start now exits non-zero with retry guidance instead of running unrecorded
    while that shutdown lock is held.
    bin/dev kill shuts that session down through its own Overmind control socket or its own process group,
    escalating from TERM to KILL only for survivors, and reports success only after positively
    observing that the owner, its process group, and its listeners are gone. Fallback port discovery
    is restricted to LISTEN sockets whose process working directory is inside the current app root;
    anything it cannot attribute is reported as a diagnostic and never signalled, and missing,
    malformed, unreadable, or foreign session state fails closed rather than falling back to a broad
    kill. The default-mode port list now covers the Shakapacker dev-server port
    (SHAKAPACKER_DEV_SERVER_PORT, then dev_server.port in config/shakapacker.yml, then 3035)
    instead of a hard-coded 3001, and a session records the ports it actually selected so a kill
    verifies those rather than re-deriving a guess. Processes that deliberately leave the group with
    setsid/daemonization remain out of scope, which is why Overmind (whose tmux server daemonizes) is
    controlled through its socket. Breaking API changes for anyone calling
    ReactOnRails::Dev::ServerManager directly: .development_processes and .kill_running_processes
    are removed, .kill_processes now returns a status symbol (and bin/dev kill exits non-zero when
    it refuses or cannot verify a shutdown, with bin/dev clean refusing to delete bundles in that
    case), .print_kill_summary takes (status_symbol, blockers_array) instead of a boolean,
    .kill_port_processes only signals listening sockets whose process working directory is inside
    the current app root, and .find_port_pids is now restricted to listening sockets (it returns
    those listeners other than pid ≤ 1 and the calling process, and does not filter by app root —
    attribution happens in the kill path). Fixes
    #4846.
    #4937 and
    #4964 by
    justin808.

  • [Pro] Surfaced missing RSC loadable stats in Node Renderer logs: The first missing
    loadable-stats.json read now emits an actionable INFO diagnostic per renderer process while
    preserving fallback and retry behavior without replaying the server path to the browser. Fixes
    #4731.
    #4918 by
    justin808.

  • [Pro] OpenTelemetry propagation and existing-provider attachment now recover cleanly: Rails-to-renderer
    requests replace stale mixed-case W3C propagation headers with each request attempt's current client-span context
    without mutating caller-owned raw headers. Node Renderer attachment to an application-owned provider now rolls back
    a newly installed tracing adapter when a conflicting sub-span integration rejects installation, so initialization can
    be retried after the conflict is removed. Existing-provider mode also reports every ignored renderer-managed option
    before provider and context checks, while both provider modes share one service-name precedence implementation.
    #4956 by
    justin808.

  • [Pro] Raised the jwt floor to >= 2.8 for reliable offline license validation on Ruby 3.4+:
    jwt 2.8 first declares base64 as a runtime dependency, so the gemspec now requires jwt >= 2.8, < 4.
    jwt 3.x remains supported and 4.x remains unsupported. Documentation now distinguishes the Pro Node renderer's
    default Fastify 5 setup, which requires Node 20+ at startup, from its engines.node floor of Node 18.19.0+
    when applications use the documented Fastify 4-compatible dependency overrides. Fixes
    #4730.
    #4864 by
    justin808.

Added

  • [Pro] React 19.3 support with react-on-rails-rsc@19.3.1-rc.0: The node renderer startup
    check and react_on_rails:doctor now accept react-on-rails-rsc 19.3.1-rc.0 (npm next) and later
    19.3.1+ releases with React/React DOM 19.3.x. The react-on-rails-pro optional peer range admits
    ~19.3.1-rc.0. Published react-on-rails-rsc 19.3.0 (npm latest, which bundles React 19.2.8 Flight)
    stays supported with React 19.2.8+ only, and 19.2.x stays on React 19.2.7+. Each check rejects mixed pairs
    such as 19.3.0 with React 19.3 or 19.2.x with React 19.3, and it rejects prerelease React builds. Doctor's
    Fix command names the React version that matches the installed RSC package. The generator still installs
    stable react-on-rails-rsc 19.2.1. Action required for upgraders: apps generated by 17.1.0 pin the
    superseded react-on-rails-rsc 19.3.0-rc.4, which the node renderer now refuses at startup. Install 19.3.0
    with React/React DOM 19.2.8+, or 19.3.1-rc.0 with React/React DOM 19.3.0. The node renderer error and Doctor's Fix name the stable
    release to install for the known 19.3.0 transition; other rejected prereleases no longer imply that
    a compatible stable version is published. Doctor falls back to the published generator pin for those versions.
    #5094 by
    justin808.

Don't miss a new react_on_rails release

NewReleases is sending notifications on new releases.