Choosing Between Turbo Drive, Frames, and Streams in Rails: A Practical Decision Guide
Choose Turbo Drive for navigation, Frames for scoped updates, and Streams for real-time features. Each tool has distinct trade-offs around WebSocket requirements, update scope, and third-party script compatibility.
31 Oct 2025, 23:03 UTC

The Decision: Incremental Page Updates Without Custom JavaScript
When building a Rails application with Hotwire, you need to choose how to update parts of the page without writing custom JavaScript. Turbo Drive, Frames, and Streams each solve different problems with distinct trade-offs. This guide helps you pick the right tool based on your requirements.
Feature Comparison at a Glance
| Feature | Turbo Drive | Turbo Frames | Turbo Streams |
|---|---|---|---|
| Update scope | Entire page body | Scoped to frame element | Fine-grained DOM operations |
| Requires WebSocket | No | No | Yes (or HTTP polling fallback) |
| Best for | Navigation and form handling | Independent page sections | Real-time collaborative updates |
| JavaScript needed | Only for third‑party integration | Only for third‑party integration | Only for third‑party integration |
Understanding the Trade-offs
Turbo Drive: Replace Full Reloads
Turbo Drive intercepts link clicks and form submissions, sending them as AJAX requests. The server returns HTML, which Turbo swaps into the page. This preserves DOM state like scroll position and form values.
Limitation: Any third‑party script that expects a full page reload (e.g., analytics, ad widgets) will break. You must listen for the turbo:load event to reinitialize such scripts.
Turbo Frames: Isolated Updates
A <turbo-frame id=…> wraps a section of the page. Links and forms inside the frame trigger updates only to that frame, leaving the rest of the page untouched. This is ideal for widgets like comment sections or product listings that update independently.
Limitation: Each frame needs a unique ID. Duplicate IDs cause Turbo to target the first matching frame, leading to unexpected behavior.
Turbo Streams: Real-time DOM Changes
Turbo Streams receive <turbo-stream> elements over a WebSocket connection (via Action Cable). Each stream specifies an operation: append, prepend, replace, remove, or update. The client applies the change without a full page refresh.
Limitation: Requires a persistent WebSocket connection. If the connection drops, you need a fallback such as HTTP polling, which adds latency and server load.
Concrete Implementation Example
Consider a chat interface where messages appear in real time. You want to avoid writing JavaScript but still show new messages instantly.
Step 1: Set Up Action Cable
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat"
end
end
Step 2: Broadcast Messages as Turbo Streams
# app/models/message.rb
class Message < ApplicationRecord
after_create :broadcast_to_turbo
private
def broadcast_to_turbo
broadcast_append_to :chat, partial: 'messages/message', locals: { message: self }
end
end
Step 3: Render the Stream in the View
<%= turbo_stream_from 'chat' %>
<div id="messages">
<%= render @messages %>
</div>
Step 4: Verify the Setup
- Start the server and open the page in two browsers.
- Create a message in one browser.
- The message should appear instantly in the other browser without a page refresh.
Validation Checklist
- For Turbo Drive: Navigate between pages and confirm the URL changes without a full reload. Check the browser console for
turbo:loadevents. - For Turbo Frames: Wrap a section in <turbo-frame id="test">. Click a link inside the frame and verify only that frame updates while the rest of the page remains unchanged.
- For Turbo Streams: Open a WebSocket channel, broadcast a stream action from the server, and observe the corresponding DOM change without a page refresh.
Final Recommendation
If you need simple navigation and form handling, start with Turbo Drive. It works with any HTML response and requires no additional infrastructure.
If you have independent page sections that update on their own, use Turbo Frames. They provide scoped updates with minimal setup.
If you need real-time updates pushed from the server, choose Turbo Streams. They excel at collaborative features but require WebSocket infrastructure.
In many applications, a combination works best: Turbo Drive for navigation, Frames for modular widgets, and Streams for live data.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.