ruby-on-railsturbolinkshotwire-rails

Turbolinks to Turbo upgrade has broken form redirection


I'm attempting to upgrade to Turbo from Turbolinks and I've found that the client is not rendering redirects for form submissions.

Versions:

I've ignored the incompatibility between Turbo and Devise for now - just trying to get regular forms working without having to disable Turbo on them.

Here's an example action:

def update
  authorize @label
  @label.update(label_params)
  if @label.save
    redirect_to document_labels_path(document_id: @document.id)
  else
    render :new, status: :unprocessable_entity
  end
end

Here's a rendered form:

<form class="simple_form new_label" id="label_form" novalidate="novalidate" action="/documents/72/labels" accept-charset="UTF-8" method="post">
...
</form>

When submitting a valid form, the server will say Processing by LabelsController#create as TURBO_STREAM and correctly serve a 302. It will then serve the 200 for the redirect location. The browser however is left just looking at the submitted form. Changing the redirect status to 303 doesn't change anything.

I added a console.log for every Turbo event:

document.addEventListener("turbo:load", function () {
  console.log('TURBO:LOAD')
})
document.addEventListener("turbo:click", function () {
  console.log('TURBO:CLICK')
})
document.addEventListener("turbo:before-visit", function () {
  console.log('TURBO:BEFORE-VISIT')
})
document.addEventListener("turbo:visit", function () {
  console.log('TURBO:VISIT')
})
document.addEventListener("turbo:submit-start", function () {
  console.log('TURBO:SUBMIT-START')
})
document.addEventListener("turbo:before-fetch-request", function () {
  console.log('TURBO:BEFORE-FETCH-REQUEST')
})
document.addEventListener("turbo:before-fetch-response", function () {
  console.log('TURBO:BEFORE-FETCH-RESPONSE')
})
document.addEventListener("turbo:submit-end", function (event) {
  console.log('TURBO:SUBMIT-END')
  // event.detail
})
document.addEventListener("turbo:before-cache", function () {
  console.log('TURBO:BEFORE-CACHE')
})
document.addEventListener("turbo:before-stream-render", function () {
  console.log('TURBO:BEFORE-STREAM-RENDER')
})
document.addEventListener("turbo:render", function () {
  console.log('TURBO:RENDER')
})

This is what the output is for a successful form submission:

TURBO:BEFORE-FETCH-REQUEST
TURBO:SUBMIT-START
TURBO:BEFORE-FETCH-RESPONSE
TURBO:SUBMIT-END

There is no render event. Investigating event.detail.fetchResponse.response for turbo:submit-end it seems to be perfectly aware that the client should redirect, it just didn't.

Response {type: "basic", url: "http://lvh.me:3000/documents/72/labels", redirected: true, status: 200, ok: true, …}
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: true
status: 200
statusText: "OK"
type: "basic"
url: "http://lvh.me:3000/documents/72/labels"
__proto__: Response

Update: It is actually performing the redirect and the server is generating the response. The issue is that the client is not rendering the redirect response.


Solution

  • Jeff's answer is correct but I wanted to share the specific fix for the issue I was having.

    If you use HAML or Slim, I've seen it on more than one codebase where developers rename all template files .haml instead of .html.haml (same for Slim). It's never bitten me before using Turbo, but without .html in the filename, part of Rails won't know what format to serve a response in, so it defaults to the request format.

    Turbo makes a turbostream request when submitting a form, but if the response is a redirect, it expects it to be text/html in order to render it. If it receives a turbostream response to a redirect request, Turbo just sits there doing nothing with no console errors or warnings (terrible default behavior IMO).

    So if your templates do not include .html, just add it back and Turbo will render redirects. You may still need status: :see_other.

    More information:

    https://github.com/hotwired/turbo-rails/issues/122

    https://github.com/hotwired/turbo-rails/issues/287