turbo-rails: Turbo new install - forms do not redirect

I just installed Turbo in an existing Rails 6 app and all my existing forms are broken. They submit as expected but the redirect after doesn’t happen. I’m able to interact with Turbo as expected - ie, I have a frame loading correctly, so it appears I loaded Turbo correctly in Webpack.

import "@hotwired/turbo-rails"

And with just a very simple form (using slim and simple_form):

= simple_form_for @comment do |f|
  = f.input :text, label: "Comment"
  = f.button :submit

My controller performs the redirect (using responders):

class CommentsController < ApplicationController
  load_and_authorize_resource

  def index
    @comment = Comment.new
  end

  def create
    @comment.save
    respond_with @comment, location: comments_path
  end

  private

  def resource_params
    params.require(:comment).permit(
      :text
    )
  end
end

The comment gets created and the request for the redirect URL happens but the redirect does not. I have to refresh to see the changes.

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 15
  • Comments: 76 (13 by maintainers)

Commits related to this issue

Most upvoted comments

Hello everyone,

I opened #152 earlier, but I think this issue is the same. For me it is completely not correct that Turbo prevents plain HTML forms from working with this gem: <form action="/url" method="POST">...</form>

It is old good HTML 1.0 and it gets broken unless you wrap it into <turbo-frame> and/or make explicit turbo_stream response! For me it doesn’t matter what status code Rails return (though correct codes are a good practice), it is just broken HTML standard. Thus Turbo doesn’t do progressive enhancement, it forces you into yet another ecosystem, like React, Angular and other vendor-lock tools.

This is not correct. Forms must work the same way as links. We don’t need to opt-out from turbo on every link, so we shouldn’t do it for every form. Links are smart enough to use whole page as response when not wrapped in turbo_frame and so should forms.

I suggest the following logic:

  1. Form tells server that it is capable of accepting turbo response (already done)
  2. On server if turbo_stream response is not defined, then use HTML response (TBD)
  3. Response may contain Turbo stream data-only or whole page, whatever - just like links work (already done for hyperlinks, for forms it works only if it is wrapped in frame, otherwise empty response is returned for some reason)
  4. After getting a response, client either redirects or renders HTML with the same logic as links do: if request was done from turbo-frame then replace frame. Otherwise replace whole HTML page. (already done for hyperlinks)

We will get everything working for all existing and all non-Rails code out of the box. It will respect old good HTML basics. And it will be progressive enhancement, not a mandatory Turbo lock-in. It will be effortless magic!

I think it is a more high-level problem of current implementation which will solve original redirect issue, too. It looks more server-side.

Sorry if I’m missing some big idea or technical restriction, but this part is confusing.

To add to what @dmitry-rychkov said, this would also improve the Turbolinks-to-Turbo upgrade path quite a lot.

Most of it was straightforward, but having form submissions suddenly doing nothing (even without remote: true and even with status: 303) was quite a head-scratcher. The docs also seem to say that you can progressively use just Turbo Drive and later opt into more fanciness with Frames/Streams, but reality currently seems to be that you can’t use just Turbo Drive on its own for form submissions.

I got rid of Rails UJS since I was told it would conflict with Turbo and wrapped the form in a turbo_frame_tag. Displaying form errors works but the browser following the redirect isn’t. The request format is turbostream and the response is in a format.html block. Like everyone else says, the browser is left hanging on the old page with the submitted form.

The really confusing thing to me is that the server renders the redirected page, which means the browser made the request for the redirect but didn’t render it. I setup a bunch of logging on all the Turbo events and found that there’s 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 doesn’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

I wrote this SO question and got some insight. Changing format.html { redirect_to whatever_path } to format.html { redirect_to whatever_path(format: :html) } does actually change the behaviour despite the common format block. It went from hanging on the previous page with the submitted form to replacing the contents of the turbo_frame with nothing and the console complained about: Response has no matching <turbo-frame id="new_label"> element. I guess it’s passing the format along as turbostream to the redirect despite entering the format.html block. The fallback to handling turbostream requests with html responses seems to put us in a weird place when upgrading.

The handbook says:

After a stateful request from a form submission, Turbo Drive expects the server to return an HTTP 303 redirect response, which it will then follow and use to navigate and update the page without reloading.

That just doesn’t seem to be happening. It’s following the redirect, making the request with the turbostream content_type, then not rendering the result.

The confirmed post also shows that this is the convention rails expects.

TL;DR is at the bototm of this post.

@kiddrew yes! Happy to explain. @archonic I agree. This stuff is super hard to debug too because of the massive amounts of indirection in turbo and rails.

Media type registration

So in the new turbo you will find this piece of code:

https://github.com/hotwired/turbo-rails/blob/102e919318ec528b9f5908e10d6427847d7a197b/lib/turbo/engine.rb#L50-L52

This ensures you can call https://example.org/your/endpoint.turbo_stream to forgo content-negotiation, in the same way :html is registered to facilitate https://example.org/your/endpoint.html. It also allows you to write code like this:

respond_to do |format|
  format.turbo_stream do
    # respond to a turbo stream request
  end
  format.html do 
    # respond to an html request
  end
  format.any do
    # anything else
  end
end

When is the turbo_stream block executed? It’s executed when rails understands that it “should”.

  1. Because the format is explicitly set in the URL (.turbo_stream). This is not content negotiation.
  2. Following content negotiation, meaning the Accept header contains the text/vnd.turbo-stream.html media type and it is more important than anything else acceptable. In the example case, because of .any any valid media type is acceptable.

When a block is executed, it will by default set the content-type to whatever format was accepted. So, if the turbo_stream is executed, the Content-Type is set to text/vnd.turbo-stream.html if some code requests it.

Renderer creation

Often, you will not have any respond_to block in your code, but rather you’ll do something like:

render json: my_json

# or

render plain: my_plain

This internally uses a Rails Renderer instead. So for example, if you want to be able to write render csv: my_csv_obj and that object be turned into csv content by calling to_csv on it, you could do something like this:

ActionController::Renderers.add :csv do |obj, options|
  filename = options[:filename] || 'data'
  str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
  send_data str, type: Mime[:csv],
    disposition: "attachment; filename=#{filename}.csv"
end

For turbo, the code is:

https://github.com/hotwired/turbo-rails/blob/102e919318ec528b9f5908e10d6427847d7a197b/lib/turbo/engine.rb#L55-L60

What this shows is: return the content to render verbatim, which means you need to do render turbo_stream: valid_html. It will, however, set the Content-Type to text/vnd.turbo-stream.html unless it’s already set.

Default render

These two things together may help you understand what happens when you do not have respond_to blocks and do not call render turbo_stream: .... but rather have the following:

def show
 # render
end

Either explicit render call without a renderer option, or no render call at all (implicit render).

What happens under the hood is a chain of calls that effectively call render_to_body, usually on ActionView:

https://github.com/rails/rails/blob/04972d9b9ef60796dc8f0917817b5392d61fcf09/actionview/lib/action_view/rendering.rb#L108-L124

def _render_template(options)
  variant = options.delete(:variant)
  assigns = options.delete(:assigns)
  context = view_context

  context.assign assigns if assigns
  lookup_context.variants = variant if variant

  rendered_template = context.in_rendering_context(options) do |renderer|
    renderer.render_to_object(context, options)
  end

  rendered_format = rendered_template.format || lookup_context.formats.first
  @rendered_format = Template::Types[rendered_format]

  rendered_template.body
end

This in turn finds its template via context.in_rendering_context which comes from ActionView as well: https://github.com/rails/rails/blob/04972d9b9ef60796dc8f0917817b5392d61fcf09/actionview/lib/action_view/base.rb#L257-L274

def in_rendering_context(options)
  old_view_renderer  = @view_renderer
  old_lookup_context = @lookup_context

  if !lookup_context.html_fallback_for_js && options[:formats]
    formats = Array(options[:formats])
    if formats == [:js]
      formats << :html
    end
    @lookup_context = lookup_context.with_prepended_formats(formats)
    @view_renderer = ActionView::Renderer.new @lookup_context
  end

  yield @view_renderer
ensure
  @view_renderer = old_view_renderer
  @lookup_context = old_lookup_context
end

…which finally uses the lookup_context to see which templates are really available. It will use the provided views_paths (usually a list of all the view paths in your project) to find files and tries to match these things:

https://github.com/rails/rails/blob/04972d9b9ef60796dc8f0917817b5392d61fcf09/actionview/lib/action_view/lookup_context.rb#L43-L52

  • locale (like en)
  • formats (like html)
  • variants (like mobile)
  • handlers (like haml)

We can go deeper, but the important thing to understand is that a file index.html+mobile.haml will be _processed (handled) by haml, only be matchable if the variant requested is mobile, and will output html (because that is the format).

The matched template will actually set the format (see @rendered_format above).

Turbo, and why index.haml fails

Okay. Final step. Turbo doesn’t use native browser navigation but rather a fetch(y) request. It will create a special request. This will happen first when a form is being submitted, first via requestStarted which dispatches the turbo:submit-start event, and then via start, which, if allowed (confirm) will call the fetch request perform function.

(We’re almost at the point where everything lines up).

This function calls prepareHeadersForRequest on the form submission which basically checks if the form will understand a Turbo Streams response and if so adds text/vnd.turbo-stream.html to the Accept header. If it doesn’t, then it only accepts text/html, application/xhtml+xml.

WHY does it accept turbo streams? Because of this line: https://github.com/hotwired/turbo/blob/256418fee0178ee483d82cd9bb579bd5df5a151f/src/core/drive/form_submission.ts#L224

  • The form submission is not idempotent (aka a POST) <-- this.
  • Or some data attribute is present

Your controller does it’s action and then redirects to a new location by passing the location header. After it has redirected, it renders the index action and the response is sent back to the client, right?

The code that determines what gets called when it returns can be found here: https://github.com/hotwired/turbo/blob/256418fee0178ee483d82cd9bb579bd5df5a151f/src/core/drive/form_submission.ts#L180-L196

Either it succeeds or it fails, the delegated functions are here: https://github.com/hotwired/turbo/blob/aeeaae8edb5f6ec6ef6b19eaf93dc060a1e67b10/src/core/drive/navigator.ts#L92-L126.

The particular lines that we care about are:

const responseHTML = await fetchResponse.responseHTML
if (responseHTML) {
  // do stuff
}

That responseHTML property is defined here: https://github.com/hotwired/turbo/blob/aeeaae8edb5f6ec6ef6b19eaf93dc060a1e67b10/src/http/fetch_response.ts#L50-L56

get isHTML() {
  return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/)
}

get responseHTML(): Promise<string | undefined> {
  if (this.isHTML) {
    return this.response.clone().text()
  } else {
    return Promise.resolve(undefined)
  }
}

This basically says: if the content type is text/html or the xhtml equiv, then I know it’s HTML and I can interpret it. In that case only process the response and let turbo visit the new content. It does match text/vnd.turbo-stream.html as well, (and anything that ends with html).

Okay. So. Why doesn’t it then?

  • You use implicit render in your controller
  • Implicit render will try to match the current request which has Accept: text/vnd.turbo-stream.html, text/html, application/xhtml+xml. The order is significant.
  • It cannot find any file that has a format of .turbo_stream or .html. So then it tries to look for a less specific thing. It finds a file without format called index.haml.
  • When the template is picked it will use the file’s format OR THE FIRST FORMAT FROM THE ACCEPT HEADER (refresher: rendered_format = rendered_template.format || lookup_context.formats.first)
  • When you’re using implicit render, it will USE THE FORMAT THAT THE RENDERER RENDERED.
  • The haml is rendered with Content-Type: text/vnd.turbo-stream.html.
  • Turbo receives the HTMl and starts interpreting it as a stream
  • …a stream requires something to stream into, but you’re rendering a document.
  • It doesn’t render.

Hope it helps.

TL;DR

  1. Turbo adds support for turbo streams because it wants to be able to respond to a stream response which is allowed because the form is a POST.
  2. It does this by adding a media type to the Accept header.
  3. Your files always assumed you’re rendering a single media type (called action.handler instead of action.format.handler).
  4. Rails will always use the first format if none was given in a file name.
  5. Because turbo stream value is prepended, your rendered view will be rendered as (Content-Type) a turbo stream.
  6. Turbo will not allow you to render the response because it thinks it’s a stream responses, without a place to stream to and because it can only render a document HTML.

I had to explicitly use render ..., status: :unprocessible_entity to make the page show with errors and redirect ..., status: :see_other to make the page redirect.

Perhaps that helps?

Here is a quick fix for those who came there:

  1. Set config.action_view.form_with_generates_remote_forms = true at your initializer e.g. config/initializers/new_framework_defaults_6_1.rb
  2. Create a concern at app/controllers/concerns/turbo/redirection.rb
module Turbo
  module Redirection
    extend ActiveSupport::Concern

    def redirect_to(url = {}, options = {})
      turbo = options.delete(:turbo)

      super.tap do
        visit_location_with_turbo(location, turbo) if turbo != false && request.xhr? && !request.get?
      end
    end

    private

    def visit_location_with_turbo(location, action)
      visit_options = {
        action: action.to_s == 'advance' ? action : 'replace'
      }

      script = []
      script << 'Turbo.clearCache()'
      script << "Turbo.visit(#{location.to_json}, #{visit_options.to_json})"

      self.status = 200
      self.response_body = script.join("\n")
      response.content_type = 'text/javascript'
      response.headers['X-Xhr-Redirect'] = location
    end
  end
end

It’s just a copy of the redirection handler from turbolinks-rails gem 3. Add Turbo to the window at your application.js

import { Turbo } from "@hotwired/turbo-rails"
...
window.Turbo = Turbo
  1. Include concern at your ApplicationController
class ApplicationController < ActionController::Base  
  include Turbo::Redirection
  ...

That’s it. Your legacy code works.

@seanpdoyle the problem is when migrating larger apps there is a lot of complex rails UJS code, which is impossible to migrate on turbo_streams because UJS is more powerful DOM manipulations wise.

So to make it work some forms need to be remote with data-turbo=false. Disabling the turbo enables old UJS code to continue working. But, trurbo-rails gem is missing this piece from the turbolinks, hence redirects are not working from the JS request.

This is maybe a different issue from the one @kiddrew posted.

I tried modifying the response to use :unprocessable_entity and :see_other and it didn’t fix my issue. The submit and subsequent get request both work as expected but the form render or redirect doesn’t happen.

I’ve just gone through every comment and I think my problem differs in a way that my create action was correctly redirecting with turbo-rails 1.3.3 but stopped doing so after upgrading it. After upgrading the gem to 1.4.0 I can still see the 302 response but it’s actually staying in the same view and updating the turbo-frame with “Content missing”. Here’s the code:

def create
    @msg = current_user.msgs.new(msg_params)

    respond_to do |format|
      if @msg.save
        format.html { redirect_to msgs_path, notice: 'Message was created.' } # <-- this was fine in 1.3.3
      else
        format.turbo_stream do
          flash.now[:alert] = "An error occurred"
          render turbo_stream: render_error
        end
        format.html do
          redirect_to msgs_path, alert: "An error occurred"
        end
      end
    end
  end

As you can see it’s pretty basic and I just have an html response when a message is saved. Of course the form is submitting a POST.

@SleeplessByte That is correct. It’s not well documented yet, but Rails is switching to returning a 422 on fail: https://github.com/rails/rails/pull/41026

Devise will be ready for this change soon: https://github.com/heartcombo/devise/pull/5340

Really excited to start playing with these new toys 🎉

Thanks. Followed your instructions at https://github.com/hotwired/turbo-rails/blob/main/UPGRADING.md and tried so many ways to make it work - it still won’t render the redirects 😦. Only solution for me is to opt out of Turbo for all my forms using data-turbo='false'. Hoping for a fix in the future, because I like the lazy loading and the general idea.

For what it’s worth, the solution in issue 138 in hotwired/turbo worked for me - simply setting data-turbo-frame = "_top" in the form. Full credit goes to @inopinatus.

That enables your form to accept responses which don’t contain a turbo-frame and properly render the redirect.

Is anyone else experiencing an issue where they want to use turbo-confirm to add a confirmation dialog before archiving a record, while also keeping turbo: false so they can redirect to a new page if the record is saved? The form is within a turbo_frame_tag.

more than a year later and redirection still doesn’t work with form when using turbo by default in rails 7, anyone got a solution?

Yes and no.

Prepending signals “I prefer a turbo stream response” which you do. If you’d append it and the controller can return a html response, it will, despite a turbo stream response being available.

The real issue is that rails is defaulting to turbo stream implicitly. That’s the real bug!

Edit: rails is returning a full document as a stream which it should do imo. Unless you explicitly opt in.

@danielricecodes what’s the file names of the views? They are probably .html.erb.

What are your file names?

This was actually the problem. Wow 🤯

My editor was saving files with only the .slim extension. Explicitly using .html.slim fixed my app.

@kiddrew is your simple_form_for inside any turbo-frame? if so, turbo will load the redirect via ajax and not do any actual redirect or Turbo.visit().

@danielricecodes i just tried your scaffold example in a fresh rails 7 app. everything works. like with turbolinks. but there also is no turbo-frame at play.

i was scimming through turbo’s code quickly and from what i’ve seen the following happens:

everything inside a turbo-frame tag is handled through a FrameController. it’ll submit forms, detect redirects, load the resource redirected to, scan the response for turbo frames and replace those on the page. that’s why you won’t see any other change on the page.

now, for a form outside of a turbo-frame, the Navigation class will handle the submission and actually do a redirect (or execute a Turbo.visit()).

so, there you have it. you cannot break out of a turbo-frame. which is unfortunate i think.

my use case:

  • load modal content into a turbo-frame
  • do everything inside that frame (have a form, submit form, handle validation error)
  • do an actual redirect/Turbo.visit() upon successful submit

@danielricecodes what’s the file names of the views? They are probably .html.erb.

What are your file names?

@SleeplessByte - I really appreciate the thorough response but this issue is maddening. On a brand new Rails 7.0.3.1 app I used rails generate scaffold quote name:string which generates the following:

class QuotesController < ApplicationController
  before_action :set_quote, only: %i[ show edit update destroy ]

  # GET /quotes or /quotes.json
  def index
    @quotes = Quote.all
  end

  # GET /quotes/1 or /quotes/1.json
  def show
  end

  # GET /quotes/new
  def new
    @quote = Quote.new
  end

  # GET /quotes/1/edit
  def edit
  end

  # POST /quotes or /quotes.json
  def create
    @quote = Quote.new(quote_params)

    respond_to do |format|
      if @quote.save
        format.html { redirect_to quote_url(@quote), notice: "Quote was successfully created." }
        format.json { render :show, status: :created, location: @quote }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @quote.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /quotes/1 or /quotes/1.json
  def update
    respond_to do |format|
      if @quote.update(quote_params)
        format.html { redirect_to quote_url(@quote), notice: "Quote was successfully updated." }
        format.json { render :show, status: :ok, location: @quote }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @quote.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /quotes/1 or /quotes/1.json
  def destroy
    @quote.destroy

    respond_to do |format|
      format.html { redirect_to quotes_url, notice: "Quote was successfully destroyed." }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_quote
      @quote = Quote.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def quote_params
      params.require(:quote).permit(:name)
    end
end

NONE OF THE SUGGESTIONS ABOVE ARE PRESENT IN THIS FILE, YET THE FORMS AND EVERYTHING WORKS OUT OF THE BOX. I do not need to copy the view template code into this reply because it’s all unedited scaffold. In a Rails 7 scaffold controller, there is no :see_other, there are no respond_to blocks, and it is using implicit rendering…and yet…scaffold works out of the box while upgraded apps are horked. 😡

That said, it is extremely challenging to tell what the problem is with my recently upgraded Rails 7 app when such a basic scaffold works perfectly and without any of the above suggestions. My Rails 7 app has @rails/ujs removed and there are no remote forms. Nothing with Rails UJS is interfering here. For all intents and purposes, my Rails 7 app’s forms should be working but they are not.

I will keep working on it and post my solution when I find it but good gravy this issue needs to be fixed or the upgrade docs need to be a lot more clearer than they are.

@SleeplessByte thanks for the thorough explanation!

This would explain the trouble I had with Turbo. In an existing app which uses haml, upgrading to Turbo broke all forms which had a redirect response. Using status: :see_other never got the response to render. I still just have Turbo disabled on all forms which made me wonder what the point of upgrading from Turbolinks was. That should absolutely be a bug.

I had troubles with status: :unprocessible_entity not rendering any changes onto the screen (even though the HTML was returned from the server). It turns out turbo-rails require .html in the view template name. I had a bunch of new.slim or edit.slim without the .html part, and that was what was causing trouble.

Is this documented anywhere? Couldn’t find anything about the correct template naming.

If you disable turbo, it will be summited in the typical fashion. You can disable turbo on the form instead of removing it completely.

https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms

I just tried again to upgrade from Turbolinks to Turbo using the concern method that @givemetraffic posted, and all my forms are still broken. Rails version is 6.1.4. Turbo-rails is 0.5.12. Manually adding data-turbo: false to each form works.

Edit: I noticed that button_to and simple_form_for don’t use form_with, so the action_view config line doesn’t make those remote automatically. I confirmed that forms built with form_with work as expected. Every form I have is generated using simple_form or button_to, so manually updating each appears to be my only option.

A million thanks to @SleeplessByte for thoroughly demystifying this issue that folks have been battling against for—checks date when this issue was created—two years and one day! I wish there was a way to pin your answer to the top, especially since it feels like there’s unlikely to be any remediation (unless I’ve missed something).

(If you haven’t read it, read it.)

It seems like maybe not prepending the Turbo Streams content type would fix it, though, right? Assuming that would not break Turbo Streams, I would like to see that happen, and here’s why.

The reason that so many are reporting that form redirection is broken out of the box is that we are following two longstanding Rails conventions in our new apps:

  1. Redirection with implicit render
  2. Format-less template filenames

But because we now want to allow requests for Turbo Streams to work seamlessly, we’ve disrupted this pattern, so that you have to change one of those two conventions in your app just to get form submissions to work.

Wouldn’t it be better to shift the burden onto the newer feature, Turbo Streams, by appending it to the Accept header instead of prepending it?

I’ve stumbled upon this issue yesterday. My App relies on 2 format renderers in update method in controller: turbo_stream and html. In one case I do post form with rails-ujs from haml template (submit button on top of the page, above form) and CTRL instead of html renderer it always used turbo_stream. By some kind of intuition I’ve re-ordered renderers and it worked. And today in the morning I’ve discovered great explanation of why it worked - default renderer. Great job @SleeplessByte ! Thx a ton

FYI the haml / slim issue has already been confirmed here: https://github.com/hotwired/turbo-rails/issues/287.

It was exceedingly difficult to troubleshoot that issue and I only noticed because @SleeplessByte mentioned it on a thread I’ve been following for years.

If what @vikdotdev says is true (I haven’t attempted to reproduce), seems like that should be logged as a bug. A .html.slim file extension isn’t technically correct. The file isn’t HTML, it’s Slim. Slim converts to HTML but it is not HTML. Like coffee is to JS.

The solutions are in this issue.

In short: use redirect_to x, status: :see_other whenever you are redirecting from a form submission, use status: :unprocessible_entity to render errors in-line (so without redirecting), or disable turbo on the form.

A bit ridiculous that you can create forms now but can’t redirect after submission. Is there a way to get rid of this JS crap completely and do simple html redirects? I’m stuck now right now. I’ve removed turbo-rails gem from Gemfile and the forms still don’t redirect. What am I doing wrong?

I’m running into issues with this too. We have a download button that uses Rails UJS and submits a POST request to the action which redirects to an external URL for download using status: :see_other that I no longer can figure out how to get working.

Here’s the step we did for Basecamp 3 to arrive at coexistence for an app built for Turbolinks/UJS: https://world.hey.com/dhh/bringing-hotwire-to-basecamp-91a442d6

I ended up adding config.action_view.form_with_generates_remote_forms = false to my application.rb which prevented me needing to touch all forms by default

@jasonfb thank you for creating https://github.com/jasonfb/TR001 to help reproduce the issue. Would it be possible for you to alter the git history so that the changes that are tied directly to reproducing the bug behavior are their own commit? It’s very difficult to read through a commit that has a majority of its changes generated through Devise installation tasks.

After quickly scanning through, I have some high level questions:

  1. Are the form_for calls generating HTML with [data-remote="true"]?
  2. Is the format.turbo_stream { redirect_to "/nowhere" } call intentionally redirecting, or is it to demonstrate something else? Typically, redirect_to is better called from within the format.html { } block, or in a controller response that omits the format blocks entirely.
  3. Have you tried replacing the link_to "Log out" with a button_to instead?

Thanks @dixpac. Could you open a separate issue?

@seanpdoyle there is a small issue with redirects. When using Rails UJS, and setting the form as local: false, data-turbo="false" redirects are not working.

respond_to do |format|
  format.html { redirect ...} <---- This will not redirect. Request is a js.erb, worked on turbolinks
end