One of the great new features of Hotwire Turbo 7.2 was custom actions. Originally, Turbo allowed us to send stream actions to the browser to add, remove, or replace some HTML. Now, we can tell the browser to do anything: console log, set title, play sounds, and use the morphdom library for powerful changes.
There are two ways for a Rails app to emit Stream Actions:
- return one or more of them from a controller action with a
turbo_streamformat, and anturbo_stream.erbaction template file. - broadcast them to subscribers of a Turbo Stream
In this post I will recap how to send custom actions via turbo_stream action responses, and then cover the new thing I had to figure out: how to broadcast custom actions to all subscribers of a Turbo Stream.
What are the built-in actions?
Hotwire Turbo provides a small set of Stream Actions is the go-to guide.
Moar custom actions
Marco also wrote a huge library of Stream Actions you might want to use called
In your controller turbo_stream.erb response you can set the page title, and log a message to the console:
<%= turbo_stream.set_title("New Page Title goes here") %>
<%= turbo_stream.console_log("We're hiring if you can see this!") %>
Broadcasting custom actions
But what if you want to broadcast a set_title custom action to all subscribers of a stream, not just one user?
The good news is that a Stream Action that is sent to the browser via controller actions or via broadcasting is the same message. What changes is how we build the Stream Action and broadcast it.
Whilst there are nice helpers like broadcast_replace_later_to for built-in actions, I could not find an equivalently concise way to broadcast arbitrary custom actions.
As of writing, I found I had to use some low-level methods to broadcast custom actions.
class Book < ApplicationRecord
include Turbo::Streams::ActionHelper
include Turbo::Streams::StreamName
after_update_commit -> {
content = turbo_stream_action_tag(:set_title, title: "Book: #{title}")
ActionCable.server.broadcast(stream_name_from(self), content)
}
end
Send multiple Stream Actions to the same subscribers by concatenating them together:
content = turbo_stream_action_tag(:set_title, title: "Book: #{title}")
content += turbo_stream_action_tag(:console_log, message: "Book: #{title}")
ActionCable.server.broadcast(stream_name_from(self), content)
Excellent, now we can broadcast to all stream subscriber any arbitrary custom Stream Action; and thanks to Marco's turbo-power library we can now do just about anything to the browser without needing to write some bespoke JavaScript to handle it. Lovely.
Epilogue
After posting, I chatted with Marco and after a few iterations he suggested the following syntax idea that I like a lot:

SOCIAL SHARE CARD GENERATOR