Ana and you, on the same board.
Two browsers, one server (the panel on the right serves both), one database. Post a message in the top one. Your page updates. Ana's, below, doesn't know anything happened until she reloads.
So far every change started with a browser asking. A stream in a form response only reaches the browser that asked. For everyone else, the server needs a way to send without being asked: a WebSocket, a connection the page keeps open. In Rails that's Action Cable.
Subscribe the page, broadcast from the model.
<%# messages/index.html.erb %>
<%= turbo_stream_from @board %>
# app/models/message.rb
class Message < ApplicationRecord
belongs_to :board
broadcasts_to :board
end
The view helper renders a <turbo-cable-stream-source> element that opens a WebSocket subscription. The model macro renders the _message partial after every create, update, or destroy and pushes the matching stream to everyone subscribed. Flip it on, post again.
Look at Ana's response panel. Same partial.
The broadcast is a <turbo-stream action="append" target="messages"> wrapping the _message.html.erb partial, rendered on the server in a background job, delivered over the socket. Ana's Turbo applies it exactly as it would apply a form response. There's one message template in the whole app, and it just got used a third way.
# what broadcasts_to expands to, roughly
after_create_commit { broadcast_append_later_to board, target: "messages" }
after_update_commit { broadcast_replace_later_to board }
after_destroy_commit { broadcast_remove_to board }
The same macro covers update (replace) and destroy (remove), so an edit in one browser replaces the message in the other. The verbs are the eight from the playground; the transport is the only new thing. (This model only broadcasts creates; the counter in Ana's nav doesn't move, because broadcasts_to only sends the append. You'd add a second broadcast for it.)
Build it to work without the socket first.
Sockets drop: bad wifi, a server restart, a laptop lid. If the page only works when the broadcast arrives, it breaks quietly. Make every flow correct with plain requests and redirects, then add broadcasts as the upgrade. The form response already updated your page; the socket is only for the others.
And when naming every target gets tedious, remember the blunt tool from deck 13: broadcasts_refreshes sends one refresh action and every subscribed page morphs itself. Less precise, far less to maintain. Many apps never need more.