From majestic-rails
Implements Hotwire features with Turbo Drive, Turbo Frames, and Turbo Streams in Rails 8, covering morphing, broadcasts, lazy loading, and real-time updates.
How this skill is triggered — by the user, by Claude, or both
Slash command
/majestic-rails:hotwire-coderThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Hotwire sends HTML over the wire instead of JSON:
Hotwire sends HTML over the wire instead of JSON:
<body> without full page reloads<%= link_to "Download PDF", report_path(format: :pdf), data: { turbo: false } %>
<%= form_with model: @legacy, data: { turbo: false } do |f| %>
def create
@post = Post.new(post_params)
@post.save ? redirect_to(@post, notice: "Created!") : render(:new, status: :unprocessable_entity)
end
<turbo-frame id="comments">
<%= render @post.comments %>
<%= link_to "Load More", post_comments_path(@post, page: 2) %>
</turbo-frame>
<turbo-frame id="notifications" src="<%= notifications_path %>" loading="lazy">
<p>Loading...</p>
</turbo-frame>
See references/lazy-loading.md for skeleton UI patterns, infinite scroll, and Stimulus loading controllers.
<%= link_to "View Post", post_path(@post), data: { turbo_frame: "_top" } %>
<%= link_to "Results", search_path, data: { turbo_frame: "results" } %>
<turbo-frame id="<%= dom_id(post) %>">
<article>
<h2><%= post.title %></h2>
<%= link_to "Edit", edit_post_path(post) %>
</article>
</turbo-frame>
<%= turbo_stream.append "comments" do %><%= render @comment %><% end %>
<%= turbo_stream.replace dom_id(@post) do %><%= render @post %><% end %>
<%= turbo_stream.remove dom_id(@comment) %>
def create
@comment = @post.comments.create(comment_params)
respond_to do |format|
format.turbo_stream # renders create.turbo_stream.erb
format.html { redirect_to @post }
end
end
class Comment < ApplicationRecord
after_create_commit -> {
broadcast_append_to post, target: "comments", partial: "comments/comment"
}
after_update_commit -> { broadcast_replace_to post }
after_destroy_commit -> { broadcast_remove_to post }
end
<%= turbo_stream_from @post %>
<div id="comments"><%= render @post.comments %></div>
Note: For LLM streaming or features requiring message delivery guarantees, see
anycable-coderskill. Action Cable provides at-most-once delivery which can lose chunks on reconnection.
<head>
<meta name="turbo-refresh-method" content="morph">
<meta name="turbo-refresh-scroll" content="preserve">
</head>
class Post < ApplicationRecord
after_update_commit -> { broadcast_refresh_to self }
end
<%= link_to "New Post", new_post_path, data: { turbo_frame: "modal" } %>
<turbo-frame id="modal"></turbo-frame>
<div id="posts"><%= render @posts %></div>
<% if @posts.next_page? %>
<turbo-frame id="pagination" src="<%= posts_path(page: @posts.next_page) %>" loading="lazy">
<p>Loading more...</p>
</turbo-frame>
<% end %>
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Mismatched frame IDs | Silent failures | Validate IDs match |
| Missing status codes | Turbo ignores response | Use 422/303 correctly |
| Implicit locals in broadcasts | Runtime errors | Always pass request_id: nil |
Turbo.setDebug(true)
document.addEventListener("turbo:frame-missing", (e) => console.error("Frame not found:", e.detail.response))
When implementing Hotwire features, provide:
npx claudepluginhub majesticlabs-dev/majestic-marketplace --plugin majestic-railsProvides mental models, decision frameworks, and debugging patterns for Hotwire (Turbo, Stimulus, Hotwire Native) in Rails apps. Activates on common issues like Turbo Frame not updating, morphing problems, or broadcast failures.
Guides writing, reviewing, and refactoring Hotwire-powered Rails code with patterns for Turbo Drive, Turbo Frames, Turbo Streams, Turbo 8 morphing, and Stimulus controllers.
Creates Hotwire UIs with progressive enhancement in Rails: Stimulus controllers, Turbo Frame markup, Turbo Stream responses, ActionCable broadcasts, and degraded mode verification.