docs / api reference
rails ruact:doctor
rails ruact:doctor — the diagnostic task that checks a ruact installation, with --json output.
rails ruact:doctor is a diagnostic task that checks your ruact installation and reports ✓/✗ for each requirement.
rails ruact:doctorRun it after installation, after upgrading the gem (after the first JavaScript build with the new version — see the manifest check below), or whenever something is not working as expected.
What it checks
Section titled “What it checks”These are the checks an installation meets first, in the order they run:
1. Manifest
Section titled “1. Manifest”Verifies that public/react-client-manifest.json exists (or the path configured via config.manifest_path), that it is valid JSON, and that the JavaScript it describes was built by the ruact this app runs. A build by another version — or by 0.0.21 or before, which recorded none — fails: a server updated without its JavaScript breaks the pages that stream.
✓ Manifest found at /your/app/public/react-client-manifest.json (built by ruact 0.0.22)✗ Manifest not found — run vite build✗ Manifest at /your/app/public/react-client-manifest.json was built by a ruact that did not record its version (0.0.21 or before); this app runs ruact 0.0.22✗ Manifest at /your/app/public/react-client-manifest.json is not valid JSON (…)Fix: Run npm run build (production) or npm run dev (development, which generates the manifest automatically), with the ruact the app runs, and deploy the JavaScript together with the server. When vite.config.js imports ruact’s Vite plugin from another installation of the gem — what rails generate ruact:install wrote up to 0.0.21 — the failure says so: run rails generate ruact:install again, which replaces a vite.config.js it generated with one that asks Bundler for the plugin (for one you edited, it prints the change to make). -- --json reports the fix as the check’s remediation.
2. Vite
Section titled “2. Vite”Attempts a TCP connection to localhost:5173 to verify the Vite dev server is accessible.
✓ Vite accessible at localhost:5173✗ Vite not accessible at localhost:5173 — run npm run devFix: Start the Vite dev server with npm run dev or bin/dev. In production, this check is informational — Vite does not run in production.
3. Controller
Section titled “3. Controller”Reports the adoption mode: which controllers render ruact pages.
✓ island mode: 3 controllers render ruact pages (include Ruact::Controller)⚠ island mode: no controller renders ruact pages yet — add include Ruact::Controller to one, or run rails generate ruact:scaffold✓ whole-app mode: ApplicationController includes Ruact::Controller — 42 templates in app/views render through ruactThe install changes no controller, so a fresh install shows the warning until the first page exists. The whole-app count is every page template in app/views (layouts and partials excluded) — what ruact:install --app turned into ruact pages.
4. Layout
Section titled “4. Layout”Checks the layout your ruact pages actually render through, as config.layout names it. On a fresh install that is the layout ruact ships, and there is nothing of yours to check:
✓ ruact pages render through ruact's layout (config.layout = "ruact")If you render through your own layout instead (config.layout = true, or a name — including a layouts/ruact.html.erb you copied with rails generate ruact:layout), the doctor reads that file and wants a <div id="root"></div> and <%= ruact_js_assets %> in it:
✓ application.html.erb owns the document (React root + ruact_js_assets, config.layout on)⚠ layout is ready but Ruact.config.layout is false✗ application.html.erb is missing <div id="root"></div> and <%= ruact_js_assets %>Fix: add the lines it names — rails generate ruact:install prints them, and never edits your layout — or set config.layout = "ruact" to use the layout ruact ships.
5. Component CSS
Section titled “5. Component CSS”Checks that the stylesheets Vite built for your client components are linked by the layout that renders your pages. Nothing to check if the build has none, or if ruact’s own layout or the built-in shell renders them — both link it.
✓ client-component CSS is linked by ruact's layout✗ the build emits 1 client-component stylesheet(s) that nothing links — add <%= ruact_head_assets %> to application.html.erbFix: add <%= ruact_head_assets %> as the first thing inside <head>, above your stylesheet_link_tag. Without it that CSS works in development — Vite injects it — and disappears in production.
6. Streaming
Section titled “6. Streaming”Reports the current streaming mode and the detected web server.
✓ streaming: buffered (Puma)✓ streaming: enabled (Puma)When a controller streams (it declares ruact_streaming or includes ActionController::Live), the check adds where such a page is sent whole without saying so — behind a proxy that talks HTTP/1.0 to the app:
✓ streaming: enabled (Puma); 1 controller streams (ruact_streaming or ActionController::Live) — behind a proxy, the app must be reached over HTTP/1.1 (nginx: `proxy_http_version 1.1;`), or those pages are sent wholeThis check always passes — it is informational. enabled means the server can send a response as it is written (Puma); buffered means it sends a response whole. Which actions stream is the controller’s ruact_streaming (see Streaming).
7. load_async
Section titled “7. load_async”Reports whether load_async runs its query in the background — how a page starts a slow query in the controller so it finishes while the shell is sent.
✓ load_async: runs in the background (async_query_executor = :global_thread_pool)⚠ load_async: runs its query where it is called — config.active_record.async_query_executor is not set. …A warning, never a failure: an app that does not call load_async has nothing to fix. Without Active Record it passes with nothing to check. In development, the first load_async that runs in place also says so in the log.
8. Legacy constant scan
Section titled “8. Legacy constant scan”Scans config/initializers/**/*.rb and app/**/*.rb for any reference to the pre-rename gem name (regex form rails[_-]rsc) or its PascalCase constant equivalent. The gem went through a rails[_-]rsc → ruact rename between v0.0.2 and v0.0.3; a leftover reference here means a host-side migration was incomplete. When something is found, the doctor prints the exact offending string (built from fragments at runtime; see gem/lib/ruact/doctor.rb constants LEGACY_CONST and LEGACY_GEM) plus the file path and line number, so you can jump to the call site and fix it.
When all references are clear, you see a ✓ No legacy … references found line. Otherwise each hit is printed with a ✗ prefix and the source location.
Fix: replace the legacy constant prefix with Ruact (matching Ruact::Controller, Ruact::Serializable, and friends), and update the Gemfile to require gem "ruact". See the Renamed section in CHANGELOG for the full migration table.
Exit behaviour
Section titled “Exit behaviour”If every check passes (a ⚠ warning still counts as passing), ruact:doctor exits 0. If any check fails:
- Each failure is printed with
✗ - A hint is printed:
Run rails ruact:doctor -- --json for how to fix each failure - The task exits with a non-zero status (can be used in CI)
Running in CI
Section titled “Running in CI”- name: Check ruact installation run: bundle exec rails ruact:doctorThis catches misconfigured installations before they reach production.
Running programmatically
Section titled “Running programmatically”The doctor is implemented as Ruact::Doctor — extracted from the Rake task for direct testability:
passed = Ruact::Doctor.run# prints ✓/✗ for each check, returns true if all pass