Implement Hotwire
Build modern Rails frontends with Hotwire using progressive enhancement.
Quick Reference
| Need |
Hotwire choice |
| Replace part of a page after a link/form |
Turbo Frame |
| Broadcast server-side changes |
Turbo Stream |
| Client-only behavior beyond Turbo |
Stimulus controller |
| Full page navigation |
Normal Rails navigation, not a frame |
HARD-GATE
ALWAYS start with HTML-only, enhance with Hotwire progressively
NEVER use Turbo Frames for full page navigation
ALWAYS test without JavaScript first
Core Process
- Build plain HTML — implement the feature with standard Rails forms and links, no Hotwire.
- Identify update regions — wrap partial-update areas in
turbo_frame_tag. Validate: confirm <turbo-frame> appears in the DOM with the correct id.
- Add Turbo Frames / Streams — scope frame navigation or broadcast via ActionCable. Validate: confirm frame requests return
text/vnd.turbo-stream.html in DevTools Network tab; for ActionCable, verify the subscription appears in the Action Cable log.
- Layer Stimulus — attach controllers only where JavaScript behaviour is needed beyond Turbo. Validate: confirm
application.getControllerForElementAndIdentifier(el, 'name') returns the controller instance in the browser console.
- Verify degraded mode — disable JavaScript in browser DevTools (or run
rails test:system with the Capybara :rack_test driver) and confirm all hold without JS: forms submit, links navigate, data persists after reload.
Code Examples
Turbo Frame
<%= turbo_frame_tag "post_#{@post.id}" do %>
<h1><%= @post.title %></h1>
<%= link_to "Edit", edit_post_path(@post) %>
<% end %>
Turbo Stream
<%= turbo_stream.append "posts", partial: "post", locals: { post: @post } %>
Stimulus Controller
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["name"]
greet() { alert(`Hello ${this.nameTarget.value}!`) }
}
Register in app/javascript/controllers/index.js:
import GreetController from "./greet_controller"
application.register("greet", GreetController)
Output Style
When implementing Hotwire, your output MUST include:
- Progressive baseline — how the feature works with plain HTML before enhancement.
- Chosen primitive — Turbo Frame, Turbo Stream, Stimulus, or combination, and why.
- DOM contract — frame IDs, stream targets, Stimulus controller names, targets, values, and actions.
- Server contract — controller response formats, broadcast triggers, partial names, and ActionCable channel/log checks when used.
- Verification — degraded-mode checklist from Core Process step 5, plus system/browser checks for frame, stream, or Stimulus behavior.
- Language — English unless explicitly requested otherwise.
Extended Resources (Progressive Disclosure)
Load these files only when their specific content is needed:
- EXAMPLES.md — Use when you need full worked examples of Turbo Frames, Streams, and Stimulus patterns
- references/workflow.md — Use when you need the step-by-step Hotwire implementation workflow and decision tree
Integration
| Skill |
When to chain |
| write-tests |
For system specs and failing interaction coverage |
| apply-stack-conventions |
For Rails + Hotwire + Tailwind stack alignment |
| code-review |
After the UI behavior and degraded mode are verified |
1---2name: implement-hotwire3description: Use when adding Turbo Frames, Turbo Streams, or Stimulus controllers. Trigger words: Hotwire, Turbo, Stimulus, frames, streams.4license: MIT5---67# Implement Hotwire89Build modern Rails frontends with Hotwire using progressive enhancement.1011## Quick Reference1213| Need | Hotwire choice |14|------|----------------|15| Replace part of a page after a link/form | Turbo Frame |16| Broadcast server-side changes | Turbo Stream |17| Client-only behavior beyond Turbo | Stimulus controller |18| Full page navigation | Normal Rails navigation, not a frame |1920## HARD-GATE2122```text23ALWAYS start with HTML-only, enhance with Hotwire progressively24NEVER use Turbo Frames for full page navigation25ALWAYS test without JavaScript first26```2728## Core Process29301. **Build plain HTML** — implement the feature with standard Rails forms and links, no Hotwire.312. **Identify update regions** — wrap partial-update areas in `turbo_frame_tag`. Validate: confirm `<turbo-frame>` appears in the DOM with the correct `id`.323. **Add Turbo Frames / Streams** — scope frame navigation or broadcast via ActionCable. Validate: confirm frame requests return `text/vnd.turbo-stream.html` in DevTools Network tab; for ActionCable, verify the subscription appears in the Action Cable log.334. **Layer Stimulus** — attach controllers only where JavaScript behaviour is needed beyond Turbo. Validate: confirm `application.getControllerForElementAndIdentifier(el, 'name')` returns the controller instance in the browser console.345. **Verify degraded mode** — disable JavaScript in browser DevTools (or run `rails test:system` with the Capybara `:rack_test` driver) and confirm all hold without JS: forms submit, links navigate, data persists after reload.3536## Code Examples3738### Turbo Frame39```erb40<%= turbo_frame_tag "post_#{@post.id}" do %>41 <h1><%= @post.title %></h1>42 <%= link_to "Edit", edit_post_path(@post) %>43<% end %>44```4546### Turbo Stream47```erb48<%= turbo_stream.append "posts", partial: "post", locals: { post: @post } %>49```5051### Stimulus Controller52```javascript53import { Controller } from "@hotwired/stimulus"5455export default class extends Controller {56 static targets = ["name"]57 greet() { alert(`Hello ${this.nameTarget.value}!`) }58}59```60Register in `app/javascript/controllers/index.js`:61```javascript62import GreetController from "./greet_controller"63application.register("greet", GreetController)64```6566## Output Style6768When implementing Hotwire, your output MUST include:691. **Progressive baseline** — how the feature works with plain HTML before enhancement.702. **Chosen primitive** — Turbo Frame, Turbo Stream, Stimulus, or combination, and why.713. **DOM contract** — frame IDs, stream targets, Stimulus controller names, targets, values, and actions.724. **Server contract** — controller response formats, broadcast triggers, partial names, and ActionCable channel/log checks when used.735. **Verification** — degraded-mode checklist from Core Process step 5, plus system/browser checks for frame, stream, or Stimulus behavior.746. **Language** — English unless explicitly requested otherwise.7576## Extended Resources (Progressive Disclosure)7778Load these files only when their specific content is needed:7980- **[EXAMPLES.md](EXAMPLES.md)** — Use when you need full worked examples of Turbo Frames, Streams, and Stimulus patterns81- **[references/workflow.md](references/workflow.md)** — Use when you need the step-by-step Hotwire implementation workflow and decision tree8283## Integration8485| Skill | When to chain |86|-------|---------------|87| **write-tests** | For system specs and failing interaction coverage |88| **apply-stack-conventions** | For Rails + Hotwire + Tailwind stack alignment |89| **code-review** | After the UI behavior and degraded mode are verified |