The same respond_to, a second format
Every broadcast in the last course fired without any request: a push from the server, with nobody asking. This course's turbo stream answers a question instead. An ordinary form submission, over ordinary HTTP, gets its answer from format.turbo_stream, one branch of the same respond_to block Rails has always used for format.html and format.json.
The smallest example in this app
app/controllers/locales_controller.rb, in full, unedited:
class LocalesController < ApplicationController
def update
locale = params[:locale]
if I18n.available_locales.include?(locale.to_sym)
I18n.locale = locale.to_sym
session[:locale] = locale
end
respond_to do |format|
format.turbo_stream
format.html { redirect_back fallback_location: root_path }
end
end
end
Switching the site's language is one PATCH request to this action. format.turbo_stream with no block means what format.html meant in the last course's messages#create doc example: render the matching template by convention, here app/views/locales/update.turbo_stream.erb, and send it back with the right content type.
What decides which branch runs
The client's request carries an Accept header, and respond_to picks the format block that matches it. When Turbo submits a form with any method other than GET, it puts text/vnd.turbo-stream.html in front of the usual text/html in that header. The locale switcher isn't a form, though. A Stimulus controller, ui--locale-switch, sends the PATCH itself with fetch, asks only for text/vnd.turbo-stream.html, and hands the response to Turbo.renderStreamMessage. Either way the client says it understands streams, and format.turbo_stream answers. A client that never says so, such as curl, falls through to format.html: the same redirect_back any pre-Hotwire Rails app would run.
One action, two response shapes
Nothing in LocalesController#update's own logic cares which branch runs: reading params[:locale], checking that it's valid and setting the session all happen once, above the respond_to block, the same for both. Only the response forks: a .turbo_stream.erb template for one client, a redirect for the other. The last course's format.turbo_stream example showed the same idea next to format.json and format.html. Rails picking a template from the format a client asks for isn't specific to Hotwire. format.turbo_stream is one more branch on machinery Rails has always had.
Key takeaways
format.turbo_streaminside arespond_toblock is a request-response mechanism, not a broadcast. It answers the request that triggered it, asformat.htmlandformat.jsonalways have.- This app's
LocalesController#updatepairs a bareformat.turbo_stream(renderlocales/update.turbo_stream.erbby convention) withformat.html { redirect_back ... }. - The client's
Acceptheader picks the branch. Turbo's non-GET form submissions and this app's locale switcher (a Stimulusfetch) both ask fortext/vnd.turbo-stream.html. A client that never asks for it falls through toformat.html. - The controller's own logic (validating and applying the locale) runs once, the same way, before the branch is chosen. Only the response shape differs between the two formats.
Keep going with lesson 2
Multiple actions, one response
One comment POST returns five targeted stream actions, all run in template order within one animation frame.
A free account also gives you the quizzes, your progress and notes, and the discussion.