Hotwire, Turbo & Action Cable

Reviewed & published by Brayan K

Hotwire is the Rails 7 default front-end approach: build fast, reactive interfaces by sending HTML over the wire instead of writing a heavy JavaScript single-page app.

Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll understand Turbo Drive, Frames, and Streams, push live updates with Action Cable, and add small interactions with Stimulus.

What You'll Learn in This Lesson

1️⃣ Turbo Drive and Turbo Frames

Turbo Drive intercepts links and forms so navigation feels like an SPA with no full reloads. Turbo Frames scope updates to one region — a link inside a frame replaces only that frame.

# Hotwire = HTML Over The Wire. The server sends HTML, not JSON.
# Rails 7 includes Turbo and Stimulus by default.

# Turbo DRIVE: just include Turbo and ordinary links/forms become
# SPA-like — no full page reload. No code needed; it is on by default.

# Turbo FRAMES: wrap a region so only it updates.
# app/views/posts/show.html.erb
# <%= turbo_frame_tag "comments" do %>
#   <%= render @post.comments %>
#   <%= link_to "Add comment", new_comment_path %>  # loads INTO the frame
# <% end %>

# A link or form inside a frame replaces only that frame's content,
# leaving the rest of the page untouched.

2️⃣ Turbo Streams, Action Cable, and Stimulus

Turbo Streams send HTML fragments plus an action (append, replace, remove) to change the DOM, and can be broadcast live over a WebSocket with Action Cable.

# Turbo STREAMS push HTML fragments + an action to update the DOM.
# Actions: append, prepend, replace, update, remove, before, after.

# A controller can respond with a stream:
# app/views/comments/create.turbo_stream.erb
# <%= turbo_stream.append "comments" do %>
#   <%= render @comment %>
# <% end %>

# BROADCAST in real time with Action Cable (WebSocket):
class Comment < ApplicationRecord
  belongs_to :post
  # Push new comments to everyone viewing the post, live:
  after_create_commit -> { broadcast_append_to post, target: "comments" }
end

# In the view, subscribe to the stream:
# <%= turbo_stream_from @post %>
# <div id="comments"> ... </div>

Stimulus attaches small bits of JavaScript to existing HTML using data attributes.

# STIMULUS adds modest JavaScript to existing HTML.
# app/javascript/controllers/hello_controller.js
#
# import { Controller } from "@hotwired/stimulus"
# export default class extends Controller {
#   static targets = ["name", "output"]
#   greet() {
#     this.outputTarget.textContent = "Hello, " + this.nameTarget.value
#   }
# }

# Wire it up with plain data attributes in the HTML:
# <div data-controller="hello">
#   <input data-hello-target="name" type="text">
#   <button data-action="click->hello#greet">Greet</button>
#   <span data-hello-target="output"></span>
# </div>

# No build-your-own framework: Stimulus connects controllers to markup.

Your turn. Wrap each todo in a Turbo Frame so editing swaps only that item.

Mini-Challenge: A Live List

Sketch a real-time messages feature that broadcasts new records to every viewer using Turbo Streams over Action Cable.

# 🎯 MINI-CHALLENGE: a live-updating list.
# Sketch (no need to run) a tiny "messages" feature:
#
# 1. A Message model that belongs_to :room.
# 2. broadcast_append_to the room after_create_commit, target "messages".
# 3. In the view: turbo_stream_from @room and a <div id="messages">.
# 4. Result: any browser viewing the room sees new messages appear live.
#
# ✅ Goal: real-time updates with NO custom JavaScript, just Hotwire.

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Hotwire

ConceptRails / HTML
SPA-like navTurbo Drive (default)
Partial updateturbo_frame_tag "id"
DOM actionturbo_stream.append
Subscribeturbo_stream_from @room
Broadcastbroadcast_append_to
JS behaviordata-controller="hello"

🎉 Lesson Complete!

Practice quiz

What is Hotwire?

  • an approach to building reactive apps by sending HTML, not JSON, over the wire
  • a CSS framework
  • a Ruby testing tool
  • a database engine

Answer: an approach to building reactive apps by sending HTML, not JSON, over the wire. Hotwire (HTML Over The Wire) builds modern UIs by sending HTML from the server instead of JSON.

What does Turbo Drive do?

  • stores sessions
  • runs background jobs
  • makes full-page navigation feel like a single-page app by intercepting links and forms
  • compiles Ruby

Answer: makes full-page navigation feel like a single-page app by intercepting links and forms. Turbo Drive intercepts clicks and form submits, swapping the page body for SPA-like navigation without full reloads.

What is a Turbo Frame used for?

  • writing migrations
  • updating just a portion of the page independently
  • encrypting cookies
  • defining database tables

Answer: updating just a portion of the page independently. A turbo_frame_tag scopes updates to one region so only that part of the page changes.

What do Turbo Streams deliver?

  • raw JSON arrays
  • SQL queries
  • CSS rules
  • fragments of HTML with actions like append, prepend, replace, or remove

Answer: fragments of HTML with actions like append, prepend, replace, or remove. Turbo Streams send HTML fragments plus an action telling the browser how to update the DOM.

Which Turbo Stream action adds content to the end of a list?

  • append
  • update
  • remove
  • replace

Answer: append. The append action inserts the fragment at the end of the target element.

Over what transport can Turbo Streams be pushed from the server in real time?

  • a cron job
  • a WebSocket connection via Action Cable
  • FTP
  • plain email

Answer: a WebSocket connection via Action Cable. Turbo Streams can be broadcast over a WebSocket using Action Cable for live updates.

What is Stimulus?

  • a Ruby gem builder
  • a database adapter
  • a CSS preprocessor
  • a modest JavaScript framework that attaches behavior to existing HTML

Answer: a modest JavaScript framework that attaches behavior to existing HTML. Stimulus is a lightweight JS framework that wires controllers to HTML via data attributes.

How does a Stimulus controller connect to markup?

  • through SQL triggers
  • through inline onclick handlers
  • via data-controller and data-action attributes
  • by editing routes.rb

Answer: via data-controller and data-action attributes. Stimulus uses data-controller, data-action, and data-target attributes to bind behavior to HTML.

In Rails 7, Hotwire is best described as:

  • a database tool
  • the default front-end approach
  • a deployment service
  • a deprecated plugin

Answer: the default front-end approach. Hotwire (Turbo plus Stimulus) ships as the default front-end stack in Rails 7.

What is Action Cable?

  • a Rails framework for real-time features over WebSockets
  • a CSS grid system
  • a job scheduler
  • a templating language

Answer: a Rails framework for real-time features over WebSockets. Action Cable integrates WebSockets into Rails, enabling real-time broadcasts like Turbo Stream updates.

Continue this course

Frequently asked questions

What is Hotwire and how is it different from a JavaScript SPA?

Hotwire stands for HTML Over The Wire. Instead of a heavy JavaScript front-end that fetches JSON and renders the UI in the browser, Hotwire keeps rendering on the server and sends HTML fragments to the browser, which swaps them into the page. It is made of Turbo (Drive, Frames, and Streams) plus Stimulus for the small amount of JavaScript you still need. Compared with a classic SPA, you write far less JavaScript, keep your logic and templates in Rails, and still get fast, app-like interactions. It is the default front-end approach in Rails 7, designed to deliver modern UX with server-rendered simplicity.

What is the difference between Turbo Frames and Turbo Streams?

Turbo Frames scope a part of the page so that links and forms inside the frame update only that region, leaving the rest untouched — great for things like inline editing, pagination, or tabbed content, and they are driven by the user clicking or submitting. Turbo Streams go further: they send one or more HTML fragments each paired with an action (append, prepend, replace, update, remove, before, or after) telling the browser exactly how to change the DOM, and they can target multiple elements at once. Crucially, Streams can be pushed from the server in real time over a WebSocket, so Frames are mostly request-driven while Streams enable both response-driven and live, broadcast updates.

How do Turbo Streams deliver real-time updates?

Turbo Streams can be broadcast from the server over a WebSocket using Action Cable, Rails' real-time framework. In a model you call something like broadcast_append_to after a record is created, naming a stream and a target element; in the view you subscribe with turbo_stream_from. When the event fires, Rails renders a small HTML fragment and pushes it to every subscribed browser, which applies the stream action to update the DOM instantly. The result is live features — chat messages, notifications, collaborative lists — with essentially no custom JavaScript, because the server is sending ready-to-insert HTML rather than JSON that the client must turn into markup.

Where does Stimulus fit in?

Stimulus is the modest JavaScript half of Hotwire. Rather than rendering your UI, it sprinkles behavior onto HTML the server already produced. You write small controller classes and connect them to markup using data attributes: data-controller names the controller, data-action binds events like click to methods, and data-target marks elements the controller reads or updates. This keeps JavaScript organized and reusable without taking over rendering. Stimulus handles the interactions Turbo does not — toggling menus, copying to clipboard, debouncing input — while Turbo handles navigation and DOM updates, together giving you a reactive app with very little custom front-end code.