Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
369cda2
Switch Pro renderer client to async-http
justin808 May 13, 2026
c5331c6
Document async-http renderer migration
justin808 May 13, 2026
71cc4a6
Address async-http review feedback
justin808 May 14, 2026
769e913
Tighten async-http renderer handling
justin808 May 14, 2026
cb61cfc
Harden async-http multipart handling
justin808 May 14, 2026
040c7da
Clarify async-http streaming semantics
justin808 May 14, 2026
d3bd649
Add RBS for async-http renderer client
justin808 May 14, 2026
36cc82c
Fix async-http connect timeout handling
justin808 May 14, 2026
db28110
Harden async-http Pro validation
justin808 May 15, 2026
59fb885
Clarify keep-alive warning suppression
justin808 May 15, 2026
81c1ebe
Address async-http review follow-ups
justin808 May 15, 2026
59793ed
Harden async-http review edge cases
justin808 May 15, 2026
2453fa0
Address async-http review nits
justin808 May 15, 2026
bd32f44
Clarify deprecated timeout initialization
justin808 May 15, 2026
39fadd7
Harden renderer response body handling
justin808 May 15, 2026
6e46ded
Address async-http review edge cases
justin808 May 15, 2026
7c182b2
Keep Pro benchmark server on expected port
justin808 May 15, 2026
37bd9b4
Clarify async-http renderer request boundaries
justin808 May 15, 2026
ed85fdd
Document async-http edge contracts
justin808 May 15, 2026
915c13c
Address async-http review follow-ups
justin808 May 15, 2026
0bb804f
Document async-http request-scoped behavior
justin808 May 15, 2026
2fa04ba
Track async-http connection reuse follow-up
justin808 May 15, 2026
49c3936
Address async-http review follow-ups
justin808 May 15, 2026
62d642b
Clarify async-http streaming contracts
justin808 May 15, 2026
0653d0f
Document async-http upgrade caveats
justin808 May 15, 2026
cf8bce5
Address async-http review edge cases
justin808 May 15, 2026
1e06dd9
Address async-http review follow-ups
justin808 May 15, 2026
bdcbc17
Harden async-http renderer guards
justin808 May 15, 2026
d13657c
Address async-http review follow-ups
justin808 May 15, 2026
ba9fc47
Clarify async-http upgrade caveats
justin808 May 15, 2026
6aa453f
fix: restore length-prefixed parsing in StreamRequest
justin808 May 22, 2026
3b356d9
fix(spec): restore { nil } in StreamRequest .create example
justin808 May 22, 2026
ed3308e
Skip unsupported async props CI cases
justin808 May 23, 2026
3d3ae70
Clean up HTTPX leftovers and tighten async-http error handling
justin808 May 24, 2026
edef19d
Address async-http review nits
justin808 May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ

### [Unreleased]

#### Breaking Changes

- **[Pro]** **Node renderer HTTP client now uses async-http and requires Ruby 3.3+**: React on Rails Pro replaces the HTTPX-based renderer client with an `async-http` adapter for render, streaming, and asset-upload requests. Apps using React on Rails Pro must run Ruby 3.3 or newer before upgrading. The adapter scopes HTTP clients to individual renderer requests, so it does not reuse TCP connections between Rails requests; high-latency networks or very high request rates can see extra connection and HTTP/2 handshake overhead compared with HTTPX until persistent async-http clients are added in [Issue 3283](https://github.com/shakacode/react_on_rails/issues/3283). `config.renderer_http_pool_size` now limits concurrent streams on the request-scoped client instead of sizing a persistent process-wide connection pool, and HTTPX's implicit single transport retry is no longer present for renderer connection drops. [PR 3279](https://github.com/shakacode/react_on_rails/pull/3279) by [justin808](https://github.com/justin808).

#### Added

- **`react-on-rails/webpackHelpers` subpath export with `reactDomClientWarning`**: New webpack helper export so React 16/17 consumers can suppress the harmless `Module not found: Can't resolve 'react-dom/client'` warning with a one-liner instead of remembering a regex. The require inside `reactApis` is 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 static `require('react-dom/client')` cannot be tree-shaken without breaking React 18+. Pass `reactDomClientWarning` to `ignoreWarnings` (Webpack 5 / Shakapacker) or `stats.warningsFilter` (Webpack 4 / Webpacker 5). Fixes [Issue 3137](https://github.com/shakacode/react_on_rails/issues/3137).
Expand Down Expand Up @@ -64,6 +68,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ
- **[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](https://github.com/shakacode/react_on_rails/issues/3104). [PR 3232](https://github.com/shakacode/react_on_rails/pull/3232) by [justin808](https://github.com/justin808).
- **[Pro]** **Pro migration scans TypeScript 4.7 `.mts` and `.cts` modules**: `react_on_rails:doctor` and the Pro migration rewriter now include `.mts`/`.cts` source files (and their `.d.mts`/`.d.cts` declaration counterparts) when looking for stale `react-on-rails` references, matching the existing `.mjs`/`.cjs` coverage. Fixes [Issue 3250](https://github.com/shakacode/react_on_rails/issues/3250). [PR 3334](https://github.com/shakacode/react_on_rails/pull/3334) by [justin808](https://github.com/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](https://github.com/shakacode/react_on_rails/issues/3205). [PR 3220](https://github.com/shakacode/react_on_rails/pull/3220) by [justin808](https://github.com/justin808).
- **[Pro]** **Streaming response chunks no longer duplicate leading-newline buffers**: Fixed `StreamRequest` line splitting so chunks that begin with `\n` yield an empty line instead of re-yielding the full buffered line. [PR 3279](https://github.com/shakacode/react_on_rails/pull/3279) by [justin808](https://github.com/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](https://github.com/shakacode/react_on_rails/issues/1627). [PR 3231](https://github.com/shakacode/react_on_rails/pull/3231) by [justin808](https://github.com/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](https://github.com/shakacode/react_on_rails/issues/1958). [PR 3229](https://github.com/shakacode/react_on_rails/pull/3229) by [justin808](https://github.com/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](https://github.com/shakacode/react_on_rails/issues/1746). [PR 3230](https://github.com/shakacode/react_on_rails/pull/3230) by [justin808](https://github.com/justin808).
Expand Down
22 changes: 14 additions & 8 deletions docs/oss/configuration/configuration-pro.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ ReactOnRailsPro.configure do |config|
# config.renderer_password = ENV["RENDERER_PASSWORD"]

# Set the `ssr_timeout` configuration so the Rails server will not wait more than this many seconds
# for a SSR request to return once issued.
# for a SSR request to return once issued. With the async-http renderer client, this bounds the
# entire renderer request, including streaming responses. Increase this value for long-running
# streaming SSR responses.
config.ssr_timeout = 5

# If false, then crash if no backup rendering when the remote renderer is not available
Expand All @@ -92,19 +94,23 @@ ReactOnRailsPro.configure do |config|
# Default for `renderer_use_fallback_exec_js` is true.
config.renderer_use_fallback_exec_js = false

# The maximum size of the http connection pool,
# Set +pool_size+ to limit the maximum number of connections allowed.
# Defaults to 1/4 the number of allowed file handles. You can have no more
# than this many threads with active HTTP transactions.
# With the async-http renderer client, clients are scoped to individual requests.
# For the HTTP/2 renderer connection, this limits concurrent streams on that request's client
# rather than the size of a persistent process-wide connection pool. The current adapter does
# not reuse TCP connections between Rails requests, so high-latency networks or very high
# request rates can see extra connection and HTTP/2 handshake overhead compared with HTTPX.
# Setting this value emits a warning so upgrades notice the changed semantics.
# Setting this to nil keeps the default limit; it does not make the async-http client unlimited.
# Default for `renderer_http_pool_size` is 10
config.renderer_http_pool_size = 10

# Seconds to wait for an available connection before a timeout error is raised
# TCP connect timeout in seconds. After the socket connects, request processing and streaming
# are bounded by `ssr_timeout`.
# Default for `renderer_http_pool_timeout` is 5
config.renderer_http_pool_timeout = 5

# warn_timeout - Displays an error message if a request takes longer than the given time in seconds
# (used to give hints to increase the pool size). Default is 0.25
# warn_timeout - Displays an error message if a request takes longer than the given time in seconds.
# Default is 0.25
config.renderer_http_pool_warn_timeout = 0.25 # seconds

# Snippet of JavaScript to be run right at the beginning of the server rendering process. The code
Expand Down
33 changes: 33 additions & 0 deletions docs/pro/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,39 @@ If you only use ExecJS for SSR (the default), you do not need `react-on-rails-pr

### Additional Upgrade Notes

#### Upgrading to a version with the async-http node renderer client

React on Rails Pro now uses `async-http` instead of HTTPX for Rails-to-node-renderer requests.
This affects render, streaming render, and asset upload requests.

Before upgrading:

- Run Ruby 3.3 or newer. The `async-http` dependency requires Ruby 3.3+.
- Keep the transitive `io-endpoint` gem in the 0.17.x range when updating `async-http`. Do not run
`bundle update async-http` without checking that `io-endpoint` remains compatible; the Pro client fails fast at boot
if `IO::Endpoint::Wrapper` internals change in a later `io-endpoint` release.
- Remove direct application assumptions about HTTPX-specific response or error classes in Pro renderer request paths.
- Keep `config.ssr_timeout` sized for the full renderer request. With the async-http client, this timeout bounds
connect, request write, and response streaming time.
- Treat `config.renderer_http_pool_timeout` as the TCP connect timeout. After the socket connects, renderer work is
still bounded by `ssr_timeout`.
- Treat `config.renderer_http_pool_size` as a per-request HTTP/2 stream limit, not as a persistent process-wide
connection pool size. The current async-http adapter opens a request-scoped client for each renderer request and
does not reuse TCP connections between Rails requests, so high-latency networks or very high request rates can see
extra connection and HTTP/2 handshake overhead compared with HTTPX. Setting this value now emits a warning to make
the changed meaning visible during upgrades. Setting it to `nil` keeps the default stream limit; it does not make
the request-scoped async-http client unlimited. Persistent async-http connection reuse is tracked in
[Issue 3283](https://github.com/shakacode/react_on_rails/issues/3283).
- Expect renderer connection drops to surface immediately as `ReactOnRailsPro::Error`/connection failures. HTTPX
previously performed one implicit transport retry for some connection drops; the async-http adapter uses
`retries: 0` and leaves retry policy to the existing bundle-upload retry loop and caller behavior.
- Run the node renderer client from the normal synchronous Rails request path. Async Rails servers or middleware that
call the renderer from inside an existing Async reactor without an `Async::Task.current?` context are not currently
supported because the sync fallback may create a nested reactor. Keep Falcon/async-rails deployments on the previous
HTTPX renderer client until this support is explicitly added.
- `config.renderer_http_keep_alive_timeout` remains accepted for compatibility, but it has no effect because
async-http clients are currently scoped to individual requests. Setting it now emits a deprecation warning.

#### Upgrading to 16.4.0 or later

##### JWT gem requirement
Expand Down
31 changes: 25 additions & 6 deletions react_on_rails_pro/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ PATH
react_on_rails_pro (16.7.0.rc.0)
addressable
async (>= 2.29)
connection_pool
async-http (~> 0.95)
execjs (~> 2.9)
http-2 (>= 1.1.1)
httpx (~> 1.5)
io-endpoint (~> 0.17.0)
jwt (>= 2.7)
rainbow
react_on_rails (= 16.7.0.rc.0)
Expand Down Expand Up @@ -117,6 +116,19 @@ GEM
io-event (~> 1.11)
metrics (~> 0.12)
traces (~> 0.18)
async-http (0.95.1)
async (>= 2.10.2)
async-pool (~> 0.11)
io-endpoint (~> 0.14)
io-stream (~> 0.6)
metrics (~> 0.12)
protocol-http (~> 0.62)
protocol-http1 (~> 0.39)
protocol-http2 (~> 0.26)
protocol-url (~> 0.2)
traces (~> 0.10)
async-pool (0.11.2)
async (>= 2.0)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.0.1)
Expand Down Expand Up @@ -181,13 +193,12 @@ GEM
graphiql-rails (1.10.0)
railties
hashdiff (1.1.0)
http-2 (1.1.1)
httpx (1.7.0)
http-2 (>= 1.0.0)
i18n (1.14.8)
concurrent-ruby (~> 1.0)
io-console (0.8.2)
io-endpoint (0.17.2)
io-event (1.14.2)
io-stream (0.13.0)
irb (1.17.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
Expand Down Expand Up @@ -258,6 +269,14 @@ GEM
prettyprint
prettyprint (0.2.0)
prism (1.9.0)
protocol-hpack (1.5.1)
protocol-http (0.62.2)
protocol-http1 (0.39.0)
protocol-http (~> 0.62)
protocol-http2 (0.26.0)
protocol-hpack (~> 1.4)
protocol-http (~> 0.62)
protocol-url (0.4.0)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
Expand Down
4 changes: 2 additions & 2 deletions react_on_rails_pro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ end

### Prerequisites

- **Ruby**: >= 3.0
- **Ruby**: >= 3.3
- **Rails**: >= 6.0 (recommended: 7.0+)
- **React on Rails**: >= 11.0.7 (recommended: latest)
- **Node.js**: >= 18 (for Node Renderer)
Expand All @@ -267,7 +267,7 @@ end

| React on Rails Pro | React on Rails | Rails | Ruby | React |
| ------------------ | -------------- | ------ | ------ | ------- |
| 16.x | >= 16.0 | >= 7.0 | >= 3.2 | >= 18 |
| 16.x | >= 16.0 | >= 7.0 | >= 3.3 | >= 18 |
| 3.x | >= 13.0 | >= 6.0 | >= 3.0 | >= 16.8 |

> **Note:** Pro version numbers were aligned with the core gem starting at 16.2.0. Pro 16.x is the direct successor to Pro 3.x/4.x.
Expand Down
3 changes: 0 additions & 3 deletions react_on_rails_pro/lib/react_on_rails_pro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
require "rails"
require "react_on_rails"

# Apply HTTPX bug fix for stream_bidi plugin
require "react_on_rails_pro/httpx_stream_bidi_patch"

require "react_on_rails_pro/request"
require "react_on_rails_pro/version"
require "react_on_rails_pro/constants"
Expand Down
Loading
Loading