turbo-rails

Adds Turbo Drive, Frames, and Streams to Rails apps, delivering fast HTML-over-the-wire navigation and live updates without custom JavaScript.

Library
npm
v8.0.23
2,391stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
65/100Good
Development Activity28
Maintenance48
Community84
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality75
Innovation80
Learning Curve40

Turbo Rails is the official Ruby on Rails integration for Turbo, the Hotwire library that provides page acceleration (Drive), reusable page fragments (Frames), and real-time partial updates (Streams). The npm package @hotwired/turbo-rails is the thin JavaScript bridge half of that integration: it imports @hotwired/turbo and @rails/actioncable, assigns window.Turbo, defines a <turbo-cable-stream-source> custom element for Action Cable subscriptions, and patches Turbo’s fetch pipeline so Rails’ PATCH/PUT/DELETE verb overrides survive fetch-based form submissions.

On the Ruby side, the same repository ships a Rails::Engine with view helpers (turbo_frame_tag, turbo_stream_from), a Turbo::Broadcastable concern that lets ActiveRecord models push Turbo Stream updates over Action Cable via Active Job, controller concerns for frame and native-navigation requests, and installer generators (rails turbo:install) that wire the JS package into importmap, Node/esbuild/webpack, or Bun. It is the default Turbo integration shipped with every Rails 7+ application.

What You Get

  • Turbo Drive navigation wiring, enabled or disabled per-app via a single import line
  • turbo_frame_tag / turbo_stream_from view helpers for building Frames and Streams UI
  • A Turbo::Broadcastable model concern that renders and pushes Turbo Stream updates from ActiveRecord callbacks over Action Cable
  • <turbo-cable-stream-source> custom element with connect/disconnect lifecycle handling and automatic subscription cleanup
  • Installer generators (rails turbo:install) that configure importmap, Node/Yarn, or Bun asset pipelines automatically
  • System-test helpers (connect_turbo_cable_stream_sources) that synchronize Capybara with Action Cable broadcast timing

Common Use Cases

  • Broadcasting live updates (new comments, order status changes) to connected users without hand-rolled WebSocket JS
  • Decomposing a page into independently cacheable Turbo Frames, such as a personalized toolbar beside a publicly cached page
  • Replacing data-remote=true/UJS AJAX forms with Turbo Drive’s fetch-based form submission handling
  • Powering the navigational shell of hybrid native iOS/Android apps built with turbo-ios/turbo-android
  • Testing real-time broadcast behavior in Rails system tests without flaky WebSocket timing races

Under The Hood

Architecture Turbo Rails is structured as a Rails::Engine (lib/turbo/engine.rb) with an isolated Turbo namespace that autoloads its own app/{channels,controllers,helpers,models,jobs} paths, conditionally skipping eager-load for jobs/channels when the host app lacks Active Job or Action Cable. The JS half (app/javascript/turbo/index.js) is a thin bridge: it imports the framework-agnostic @hotwired/turbo package, re-exports it as window.Turbo, defines a custom element (cable_stream_source_element.js) that lazily subscribes to Action Cable channels through a singleton consumer (cable.js), and patches Turbo’s fetch pipeline (fetch_requests.js) to reinsert Rails’ verb-override parameter into request bodies. On the Ruby side, Turbo::Broadcastable hooks ActiveRecord’s commit callbacks to render Turbo Stream templates and broadcast them through Action Cable, deferring the render-and-broadcast work to an Active Job so it never blocks the triggering request or transaction. Controllers gain concerns for detecting frame and native-navigation requests, plus a custom layout (turbo_rails/frame) that strips the app layout for XHR frame requests.

Tech Stack Two ecosystems share one repository. The Ruby side depends on actionpack and railties >= 7.1.0 and Ruby >= 3.1, using ActiveJob for asynchronous broadcast jobs and Action Cable for websocket delivery; its test suite runs Minitest against a full test/dummy Rails app with Capybara-driven system tests. This npm package has zero runtime dependencies beyond peers @hotwired/turbo (version-pinned) and @rails/actioncable (>=7.0), built with Rollup and Terser into a UMD asset-pipeline bundle while its ESM entry ships untranspiled for importmap and bundler consumers. Distribution is dual: RubyGems as turbo-rails and npm as @hotwired/turbo-rails, both installed automatically by rails turbo:install depending on which JS toolchain the host app uses.

Code Quality The repository carries an extensive Minitest suite organized by feature area (streams, frames, native, drive, refreshes, system), exercised against a real test/dummy Rails application rather than mocks, plus Capybara system tests covering actual browser behavior including the WebSocket-timing edge case the README documents explicitly. Error handling follows Rails idioms and JS guard clauses (e.g. checking customElements.get() before redefining an element); naming is consistent with each ecosystem’s conventions; CI runs via GitHub Actions. Neither side uses static typing (no Sorbet/RBS, no TypeScript), and no dedicated linter configuration is present at the repo root beyond standard Rails conventions.

What Makes It Unique The distinguishing technical choice is treating server-rendered HTML fragments as the real-time wire format instead of JSON: Broadcastable renders actual Turbo Stream template tags server-side and pushes them over Action Cable, turning a model change directly into a DOM patch with no client-side templating or state-diffing code required. Paired with the fetch-based verb-override patch that preserves Rails’ PATCH/PUT/DELETE semantics under Turbo’s fetch()-driven navigation, and a declarative custom element for subscription lifecycle, this is a genuinely distinctive HTML-over-the-wire approach relative to typical SPA/JSON-API patterns, building on ideas from Turbolinks and Hotwire rather than inventing the technique from scratch here.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search