hotwired/turbo-rails
52.7
Adequate · 19 September 2026
8.2k
lines of production code
JavaScript
with Ruby
1
measurement over time
What this system is
This system is a Ruby gem that integrates the Hotwired Turbo library into Rails applications, enabling real-time, partial page updates via Turbo Streams and Action Cable. It provides server-side helpers for broadcasting DOM changes, managing Turbo Frames with minimal layouts, and handling native app navigation, while also offering client-side utilities for request interception and stream subscription. The library supports modern JavaScript workflows through npm distribution and includes comprehensive testing infrastructure for verifying broadcast behavior and frame interactions.
How it got here
2020 — Turbo Streams migration and v8 upgrade
36 changes.
This period focused on migrating the library from the legacy Turbolinks and 'page updates' terminology to the modern Turbo Streams API, including renaming channels, helpers, and broadcast mechanisms. It also involved upgrading the core JavaScript library to version 8 with morphing support, modernizing the build system with Rollup and Importmap, and establishing comprehensive test coverage for the new streaming and frame features.
2021–2025 — Turbo Stream testing and infrastructure
24 changes.
This period focused on expanding test coverage for Turbo Streams broadcasting, form handling, and namespaced models within the dummy application. It also introduced infrastructure for debouncing, request ID tracking, and streamlined release workflows to support these features.
Features
Add Turbo installation Rake tasks
A new \turbo:install\ Rake task is introduced to simplify setting up Turbo in a Rails application. The task automatically detects the project's asset management setup—checking for \config/importmap.rb\ or \package.json\ (with optional Bun support)—and invokes the appropriate sub-task (\turbo:install:importmap\, \turbo:install:node\, or \turbo:install:bun\) to run the corresponding installation template.
lib/tasks · high confidence
Add dedicated frame layout with CSRF support
A new layout file for Turbo frame responses has been introduced, providing a minimal HTML structure that includes CSRF meta tags and yields content. This ensures that frame requests are rendered with the necessary security tokens and a clean, standalone document structure.
app/views · high confidence
Added debouncer infrastructure and bug report template
The library now includes a new \Turbo::Debouncer\ class and a \Turbo::ThreadDebouncer\ wrapper to manage delayed execution of blocks, providing a foundation for future features like page refreshes and broadcasting. Additionally, a \bug\_report\_template.rb\ file has been added to help users create reproducible Rails applications for debugging issues.
turbo-rails · high confidence
Added executable scripts for Rails, testing, and release workflows
The bin directory now includes three new executable scripts to streamline development and deployment. bin/rails provides a local entry point to run Rails commands against the dummy application, facilitating easier local testing of the engine. bin/test automates the test environment setup by installing Ruby and JavaScript dependencies, building assets, and preparing the test database before running the suite. bin/release is a new Bash script that automates the gem release process, including version bumping, git tagging, building the gem, and pushing it to RubyGems.org.
bin · high confidence
New Turbo Cable and Fetch Request utilities
This change introduces new JavaScript modules in app/javascript/turbo to enhance Action Cable integration and HTTP method handling. The new cable.js module provides a singleton consumer pattern for Action Cable, while cable\_stream\_source\_element.js adds a custom \<turbo-cable-stream-source\> web component that manages subscription lifecycle and exposes a 'connected' attribute for state awareness. Additionally, fetch\_requests.js adds support for encoding HTTP methods into request bodies via the \_method parameter, specifically respecting formmethod overrides on submit buttons (e.g., Rails FormBuilder\#button), and the index.js entry point now assigns Turbo to the window object and registers the fetch request interceptor.
app/javascript · high confidence
Removals
Removal of Turbo Updates broadcast job classes
The \Turbo::Updates::BroadcastJob\ and \Turbo::Updates::CommandBroadcastJob\ classes have been removed from the application. These jobs previously handled asynchronous rendering and command broadcasting for the \Turbo::Updates\ module; their deletion indicates that this specific asynchronous update mechanism is no longer supported or has been replaced by a different implementation.
app/jobs/turbo/updates · high confidence
Behavioural changes
Adopts minimal layout for Turbo Frame responses
Requests made within a Turbo Frame now render using a dedicated minimal layout (\turbo\_rails/frame.html.erb\) instead of skipping the layout entirely. This allows content to be rendered into the \\<head\>\ section while still optimizing performance by avoiding the full application layout. The system also detects these requests via the \Turbo-Frame\ header and exposes \turbo\_frame\_request?\ and \turbo\_frame\_request\_id\ as helper methods for use in views.
app/controllers/turbo/frames · high confidence
Documentation overhaul and build system updates
The project documentation has been significantly updated to reflect the transition from Turbolinks to Turbo, including new sections on Turbo Drive, Frames, and Streams, along with an \UPGRADING.md\ guide for migrating from previous versions and handling digest changes. The build process now utilizes Rollup to generate both standard and minified JavaScript assets with sourcemaps, and the test suite has been enhanced with a prerequisite task to install Ruby and JavaScript dependencies and build assets before running tests.
(repo-wide) · high confidence
Extracted Turbo Stream action helper with optional targets and model support
The \Turbo::Streams::ActionHelper\ module has been introduced to centralize the generation of \turbo-stream\ tags. This helper makes the \target\ and \targets\ attributes optional, allowing stream actions to be emitted without a specific DOM element when appropriate. It now supports passing Model classes or instances to the \target\ argument, automatically converting them to \dom\_id\-compatible strings, and allows \targets\ to accept arrays. A new \turbo\_stream\_refresh\_tag\ helper is also provided for page refresh actions.
app/helpers/turbo/streams · high confidence
Installer now supports Bun and Node.js package managers
The installation script has been updated to support Bun and Node.js (Yarn) in addition to the existing Importmap workflow. When installing Turbo, the system now detects the available package manager: it uses Bun if applicable, falls back to Yarn for Node.js environments, and continues to use Importmap for pinning. The installer also includes a guard to ensure a JavaScript entrypoint file exists before attempting to append the Turbo import statement.
lib/install · high confidence
Migrate Turbo Native helpers to Hotwire Native and expose public API
The native navigation helpers have been moved from the \Turbo::Links\ namespace to \Turbo::Native\ and updated to recognize both legacy "Turbo Native" and new "Hotwire Native" user agents. The \turbo\_native\_app?\ helper is now publicly exposed as a helper method (with \hotwire\_native\_app?\ as the primary name and \turbo\_native\_app?\ as an alias), allowing views to detect native clients. Additionally, the navigation actions (\recede\_or\_redirect\_to\, etc.) now accept an optional \native\_params\ argument, enabling developers to pass extra parameters to the native redirect URL.
app/controllers/turbo/native · high confidence
Native navigation routes now conditional on Turbo configuration
The three native navigation routes (recede, resume, refresh) have been updated to point to the new \turbo/native/navigation\ controller instead of the previous \turbo/links/native\_navigation\ controller. Additionally, these routes are now only drawn when the \Turbo.draw\_routes\ configuration option is enabled, allowing users to disable them entirely if needed.
config · high confidence
Removed legacy Turbo updates broadcast and tag builder classes
The \Turbo::Updates::Broadcasts\ module and \Turbo::Updates::TagBuilder\ class have been removed from the application. These components previously provided the underlying implementation for broadcasting Turbo Stream updates and building page update tags (such as \page\_update.remove\ or \page\_update.replace\). Their removal indicates a shift away from this specific internal API, likely in favor of a different update mechanism or a dependency upgrade that handles these concerns differently.
app/channels/turbo/updates, app/models/turbo/updates · high confidence
Rename page updates to Turbo Streams and add test helpers
The gem renames the legacy "page update" concept to "Turbo Stream", updating the MIME type to \turbo\_stream\, the renderer key to \turbo\_stream\, and the test assertion methods from \assert\_page\_update\/\assert\_no\_page\_update\ to \assert\_turbo\_stream\/\assert\_no\_turbo\_stream\. It also introduces new test helpers: \Turbo::SystemTestHelper\ for system tests (including \connect\_turbo\_cable\_stream\_sources\ and assertions for \\<turbo-cable-stream-source\>\ elements), \Turbo::Broadcastable::TestHelper\ for Action Cable broadcasting tests, and \Turbo::TestAssertions::IntegrationTestAssertions\ for integration tests. The \assert\_turbo\_stream\ assertion now supports a \count\ option and matches on \action\, \target\, and \targets\ attributes, while \assert\_turbo\_frame\ and \assert\_no\_turbo\_frame\ are added for frame assertions. The engine now conditionally autoloads channels and jobs only if Action Cable and Active Job are present, and configures \test\_connect\_after\_actions\ to wait for cable stream sources after \visit\ by default.
lib/turbo · high confidence
Renamed Turbo Updates to Streams and added morph action support
The Turbo updates subsystem has been renamed to streams, moving files from \app/channels/turbo/updates\ to \app/channels/turbo/streams\ and updating the underlying channel from \Turbo::UpdatesChannel\ to \Turbo::StreamsChannel\. This change introduces a new \broadcast\_morph\_to\ action alongside existing actions like replace, update, and append, and allows broadcasting actions without rendering content. Additionally, the \broadcast\_refresh\_to\ method now accepts attributes to customize the stream tag, and the \StreamName\ module includes class methods to help custom channels verify stream names from parameters.
app/channels/turbo/streams · high confidence
Renamed Turbo streams channel from Updates to Streams
The ActionCable channel responsible for delivering Turbo updates has been renamed from \Turbo::UpdatesChannel\ to \Turbo::StreamsChannel\. This change reflects a broader terminology shift from 'updates' to 'streams' within the library, updating the class name, associated modules (e.g., \Turbo::Streams::Broadcasts\), and documentation comments to align with the new naming convention.
app/channels/turbo · high confidence
Renamed page\_update to turbo\_stream for consistency
The \page\_update\ controller helper and its underlying \Turbo::Updates::PageUpdateTagBuilder\ module have been renamed to \turbo\_stream\ and \Turbo::Streams::TurboStreamsTagBuilder\ respectively. This change aligns the API with the standard Turbo Streams terminology, updating the method available in controllers from \page\_update\ to \turbo\_stream\ and adjusting the associated template format from \page\_update\ to \turbo\_stream\.
app/controllers/turbo/streams · high confidence
Route configuration and request-scoped state management
Developers can now explicitly disable Turbo's automatic route drawing by setting the new \draw\_routes\ configuration option to false. Additionally, the library introduces thread-local request tracking via a \current\_request\_id\ accessor and a \with\_request\_id\ helper method, enabling features that require identifying or scoping state to specific HTTP requests.
lib · high confidence
Streamlined broadcast jobs with deserialization error handling
The Turbo Streams broadcasting system now uses dedicated Active Job classes (ActionBroadcastJob, BroadcastJob, and BroadcastStreamJob) to handle asynchronous broadcasts. A key behavioral change is that these jobs now automatically discard themselves if a deserialization error occurs, preventing failed jobs from retrying indefinitely. This ensures more robust handling of broadcast actions, renders, and stream updates when message queue data cannot be properly interpreted.
app/jobs/turbo/streams · high confidence
Turbo Broadcastable API modernization and new capabilities
The Turbo::Broadcastable module has been updated to use modern Turbo Stream terminology, replacing 'container' with 'target' and 'updates' with 'streams' in both configuration and documentation. New features include the ability to broadcast page refreshes via \broadcasts\_refreshes\ and \broadcast\_refresh\_to\, support for rendering HTML, templates, or renderable objects directly in broadcast actions, and a new \suppressing\_turbo\_broadcasts\ context to disable broadcasts. The module also now supports namespaced targets and conditional loading of ActiveJob.
app/models/concerns · high confidence
Turbo Stream Tag Builder introduces new actions and rendering flexibility
The \Turbo::Streams::TagBuilder\ now supports \before\ and \after\ actions for inserting content relative to DOM targets, alongside existing \append\, \replace\, \update\, and \remove\ methods. The builder allows rendering records directly as stream content, supports passing a \partial\ with a block, and enables morphing via the \method: :morph\ keyword argument. Additionally, it ensures HTML format is set for responses and provides a standardized interface for targeting elements.
app/models/turbo/streams · high confidence
Turbo helpers renamed and expanded with new drive and stream capabilities
The Turbo helper modules have been refactored to align with the Turbo library's naming conventions and feature set. The old \turbo\_links\_helper\ and \turbo\_updates\_helper\ modules are removed, replaced by \turbo\_drive\_helper\ which provides new meta-tag helpers for controlling page caching (\turbo\_exempts\_page\_from\_cache\), previews (\turbo\_exempts\_page\_from\_preview\), reloads (\turbo\_page\_requires\_reload\), and page refresh behavior (\turbo\_refreshes\_with\). The \turbo\_frame\_tag\ helper now supports model objects and arrays for automatic ID generation via \dom\_id\, accepts a \target\ attribute instead of the deprecated \links\_target\, and automatically URL-encodes the \src\ attribute. Additionally, a new \turbo\_streams\_helper\ module introduces \turbo\_stream\ for generating stream actions and \turbo\_stream\_from\ for subscribing to Action Cable streams, replacing the previous \page\_update\ and \subscribe\_to\_page\_updates\_from\_signed\ methods.
app/helpers/turbo · high confidence
Turbo request ID tracking for page refreshes
Added a new controller concern, Turbo::RequestIdTracking, which automatically tracks the Turbo request ID for every action. This ensures that Turbo can correctly associate requests with their originating page, enabling proper support for page refreshes and broadcasting behaviors.
app/controllers/concerns · high confidence
Upgrade to Turbo 8.0.23 with morph-compatible frames
The bundled \app/assets/javascripts/turbo.js\ and \turbo.min.js\ have been updated to version 8.0.23. This release introduces support for morph-compatible frame reloading, allowing \turbo-frame\ elements to use the \refresh="morph"\ attribute to update content via DOM morphing instead of full replacement. The update also includes the latest fixes and stability improvements from the Hotwired Turbo core.
app/assets · high confidence
Test coverage
Add test fixtures for article form interactions; Added ImmediateDebouncer for synchronous test execution; Added Turbo::Broadcastable::TestHelper for testing Action Cable broadcasts; Added system tests for Turbo Streams broadcasting and form method handling; Added test components for renderable object support; Added test controller for user profiles; Added test coverage for API application controller; Added test coverage for Turbo Streams helpers, broadcasting, and channels; Added test coverage for broadcasting via user profile views; Added test fixture for partial with block; Added test fixtures for Articles, Comments, Messages, and Boards; Added test fixtures for Turbo Stream view helpers; Added test fixtures for Turbo::Broadcastable; Added test fixtures for admin controller and view paths; Added test fixtures for posts form and turbo stream rendering; Added test for Turbo helpers availability during initializers; Added test helper for capturing and asserting Turbo Stream broadcasts; Added test infrastructure and coverage for installer, request ID tracking, and Capybara selectors; Added test initializers for Turbo stream actions and Rails helper inspection; Added test layout for Turbo Frame responses; Added test model for namespaced user profiles; Added test models for Turbo::Broadcastable scenarios; Added test view for rendering NotificationComponent via Turbo Stream; Added tests for Turbo Drive helper meta tag controls; Added tests for Turbo Frame request handling and helper tag generation; Expanded dummy application controllers for comprehensive test coverage; Removed dummy mailer and mailbox classes; Removed legacy page\_update tests; Removed test for Turbo links helper cache control; Updated dummy app configuration for Rails 7.1 testing; Updated dummy app layout for Turbo Native detection and Importmap support; Updated dummy app to use importmap for JavaScript assets; Updated test dummy views to use Turbo helpers and syntax; Updated test environment configuration for CI and security; Updated tests for Turbo Native namespace and Hotwire branding.
Dependencies
Upgrade to Turbo 8 and Rails 7.1+ with dual npm/Ruby distribution
The gem now targets Rails 7.1+ (requiring Ruby 3.1+) and upgrades the core JavaScript library to Turbo 8.0.23, enabling dual distribution via both the npm package and the traditional asset pipeline. This update introduces Node.js \exports\ mappings for modern bundlers, updates the Action Cable dependency to version 7.0+, and refreshes the development environment with newer tooling like \importmap-rails\ and \cuprite\.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
Baseline
- First survey — no prior run to compare against. CAI 53.
Lenses
- Code Health 53
- Architecture 66
- Maturity 52
- Readiness 47
- Security 90
Changes since last survey
- 300 commits — 265 feature/other, 35 fixes
By area
- lib/turbo — 59 commits
- (root) — 58 commits
- app/assets — 41 commits
- (repo) — 32 commits
- app/models — 25 commits
- test/dummy — 18 commits
- app/helpers — 17 commits
- app/channels — 13 commits
- .github/workflows — 12 commits
- app/controllers — 7 commits
- lib/install — 5 commits
- config/routes.rb — 3 commits
- test/streams — 2 commits
- test/system — 2 commits
- .github/dependabot.yml — 1 commit
- lib/tasks — 1 commit
- lib/turbo-rails.rb — 1 commit
- test/broadcastable — 1 commit
- test/frames — 1 commit
- test/test_helper.rb — 1 commit
Notable commits
- fix: Create executable bug report Rails application (#593)
- fix: DEV-409 fixed issue reported in https://github.com/hotwired/turbo-rails/issues/409
- fix: FIx CI issues with RubyGems, add Ruby 3.2, Rails 7.0 job (#424)
- fix: Fix "'**' interpreted as argument prefix" warning (#400)
- fix: Fix #522, #535. See PR 624. (#683)
- fix: Fix Broadcastable inclusion in new Rails apps (#678)
- fix: Fix CI for ruby@3.2.x-rails@7.2.x
- fix: Fix CI with Rails main (#314)
- fix: Fix CI.
- fix: Fix System Tests in CI environment
- fix: Fix breaking in non-Zeitwerk environment (#304)
- fix: Fix debouncer tests to wait on correct debouncer (#767)
- fix: Fix deprecation in routing (#657)
- fix: Fix double registration of :turbo_stream Renderer
- fix: Fix indent
- fix: Fix missing activejob dependency (#331)
- fix: Fix sending created broadcasts refreshes to channels where no one listens
- fix: Fix stream_name calculation inside of thread (#727)
- fix: Fix the failing redis installation (#261)
- fix: Fix the submitter’s formmethod not being respected when using Rails ActionView::Helpers::FormBuilder#button formmethod option. (#445)
- …and 280 more
Architecture
- 0 containers · 1 bounded contexts · 0 dependency edges (baseline)
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
hotwired/turbo-rails was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 19 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit 37530c08780fa6f6dbb56a633de4b81169bdd174 — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-13a154b7f5d1.