The same respond_to, a second format

Free preview 3 min to read

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_stream inside a respond_to block is a request-response mechanism, not a broadcast. It answers the request that triggered it, as format.html and format.json always have.
  • This app's LocalesController#update pairs a bare format.turbo_stream (render locales/update.turbo_stream.erb by convention) with format.html { redirect_back ... }.
  • The client's Accept header picks the branch. Turbo's non-GET form submissions and this app's locale switcher (a Stimulus fetch) both ask for text/vnd.turbo-stream.html. A client that never asks for it falls through to format.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.
Lesson 1 of 7 · free

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.

Type to start searching...