The difference between Turbo Streams and Turbo Frames
… and check why 5600+ Rails engineers read also this
The difference between Turbo Streams and Turbo Frames
The first point of the Rails Doctrine is “Optimize for programmer happiness”, and I personally consider this one as the one that is responsible for the huge popularity and success of RoR. I would even go one better and say that it’s optimized for non-programmer happiness, too, as we know stories of “non-technical” people starting their online businesses with Rails. A considerable part of that is possible due to Rails’ convention over configuration, and we may have different views on this one, but that’s the way the cookie crumbles.
“Optimize for programmer happiness” is finally described as: “Optimizing for happiness is perhaps the most formative
key to Ruby on Rails. It shall remain such going forward.” And this is so true, as you can spot the idea even now with
Hotwire. You can start with Turbo Drive, which will not require changes and will give you out-of-the-box <body>
replacement without page reload. Getting things for free will for sure impact your happiness. But then you also get
tools that will be like sharp knives, and I mean turbo frames and turbo streams. I really like the below chart from
one of 37signals’ articles:
Source: https://dev.37signals.com/a-happier-happy-path-in-turbo-with-morphing/
But I think this chart is somehow “broken” for me, as even though the chart says I should be less happy using frames and streams than just body replacement, I’m still quite excited when working with them. I actually prefer them, as thanks to them, I can make exact changes on pages, which at the same time implies smaller calculations on the backend and smaller response sizes. Nevertheless, they differ, and here I would like to show you the main differences so you can use them mindfully, depending on the specific case.
Turbo Frames
Let’s start with Turbo Frames. They help you split your page into parts. The most important thing here is that you can
only replace a single frame, and by default, it’s a frame from which you make an HTTP request. So when your button is
in the Turbo Frame called projects
after submitting, if the server responds with more than one frame, only the
projects
frame will be updated. When you look into the Turbo-Frame request header, you will see the name of the frame
that would be replaced:
It is also possible that a button outside of the specific frame can trigger an update of the frame’s content, but then you have to explicitly point to that frame:
button_to "Delete", @projects.first, method: :delete, data: {turbo_frame: "projects"}
Above, in general, means that when working with frames, you always only “replace” the existing frame with new content.
Turbo Streams
Now, the most confusing part is Turbo Streams. First, you don’t need WebSockets to work with them. Turbo Streams work
with regular requests, WebSockets, and SSE (Server Sent Events). Second of all, we can think about turbo streaming as
broadcasting the data over the WebSocket, but it is also a format used in Accept
and Content-Type
headers
(text/vnd.turbo-stream.html
MIME type), so you can rely on it when serving the content on the server side:
respond_to do |format|
format.html { redirect_to projects_url }
format.turbo_stream do
render turbo_stream: turbo_stream.remove(dom_id_for(@project))
end
end
Unlike Trubo Frames, the Turbo Stream type of response allows you to manipulate multiple DOM elements in a single
response. Besides that, you are allowed to do more than just replace, as you can, for example, append
, prepend
,
update
, or remove
:
format.turbo_stream do
render turbo_stream: [
turbo_stream.prepend('ongoing_projects', partial: 'projects/kanban/ongoing_project'),
turbo_stream.remove(dom_id_for(@project))
]
end
Turbo Frames vs. Turbo Streams
Now, to sum the most important things up:
- Turbo Frames:
- You can only replace a single frame, so responding with multiple frames will have no effect.
- By default, it replaces a frame from which you are making an HTTP request, and if you are targeting the frame outside of the frame, it has to be pointed.
- By its nature, it supports only the replacement operation.
- Turbo Streams:
- You don’t need WebSockets to work with them, but using WebSockets allows you to broadcast real-time updates to all interested parties.
- Have
text/vnd.turbo-stream.html
MIME-type. - Unlike Turbo Frames, it allows the manipulation of multiple unrelated page elements in a single response.
- It supports more than just replacing, i.e., appending, prepending, removing, etc.
And just to compare them side by side:
Feature | Turbo Frames | Turbo Streams |
---|---|---|
Scope of updates | Single element | Multiple elements |
Update types | replace only | append, prepend, replace, update, remove, before, after, refresh |
Real-time updates | No | Yes, possible via WebSockets, but that’s just a bonus. |
This will help you next time you have concerns about which of those should be used in specific cases. If you have any questions, don’t hesitate to contact us.
See you!
PS. If you find a Hotwire topic interesting, you may want to check few other resources that we prepared:
- [YT] Hotwire, Turbo Drive, Frames and Streaming. Long Live Server Side Rendering with SPA experience
- [YT] Turbo Streaming AKA Broadcasting over Web Socket explained. DON’T DO THAT MISTAKE!!!
- [YT] How to implement infinite scroll pagination for a table using Rails Hotwire Turbo
- [YT] Make your tables alive with turbo streams. Redirect vs Turbo Streaming. Which one to choose?
- [Blog] How to use Hotwire Turbo Streams effectively?
- [Blog] How to add a loading animation to your turbo frame with TailwindCSS
- [Blog] Take advantage of Turbo Streams in event handlers
- [Blog] Be careful with turbo and view components