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:

You might also like