class Comment < ApplicationRecord
belongs_to :post
belongs_to :author, class_name: 'User'
# Automatically broadcast changes to all subscribers of this post
after_create_commit -> { broadcast_append_to post, target: "comments" }
after_update_commit -> { broadcast_replace_to post }
after_destroy_commit -> { broadcast_remove_to post }
validates :body, presence: true
end
<%= turbo_stream_from @post %>
<article class="post">
<h1><%= @post.title %></h1>
<%= simple_format @post.body %>
<div class="comments-section mt-8">
<h2 id="comment_count"><%= pluralize @post.comments.count, 'comment' %></h2>
<div id="comments" class="space-y-4 my-6">
<%= render @post.comments %>
</div>
<%= render "comments/form", post: @post, comment: Comment.new %>
</div>
</article>
<%= turbo_frame_tag dom_id(comment) do %>
<div class="comment p-4 bg-gray-50 rounded">
<div class="flex items-start gap-3">
<%= image_tag comment.author.avatar_url, class: "w-10 h-10 rounded-full" %>
<div class="flex-1">
<div class="flex items-baseline gap-2">
<span class="font-semibold"><%= comment.author.name %></span>
<span class="text-sm text-gray-500"><%= time_ago_in_words comment.created_at %> ago</span>
</div>
<p class="mt-1"><%= comment.body %></p>
</div>
</div>
</div>
<% end %>
Broadcasting Turbo Streams via Action Cable enables real-time collaborative features without polling. When a model is created, updated, or destroyed, I broadcast the change to all subscribed users using broadcasts_to or manual broadcast_* methods. Subscribers receive Turbo Stream fragments that update their DOM automatically. This pattern powers collaborative editing, live notifications, real-time dashboards, and multiplayer features. The key is scoping broadcasts appropriately—I use stream_for to create user-specific or resource-specific channels. I also include the current user's ID in the broadcast payload so their UI doesn't duplicate the update they just made. Proper authorization ensures users only subscribe to streams they're allowed to see.