Is it a good idea to respond with a turbo_stream when a form is invalid?

In a typical set up you might do:

    if @checkout.valid?
      respond_to do |format|
      render :new, status: :unprocessable_entity

This way the happy path is efficient and if there’s errors in the submission you can correctly let the client know things went wrong by sending a 422 back and display the errors in the form. This is what you’ll find in most guides.

However you can also do this and return a turbo_stream for both cases:

    respond_to do |format|

You lose the 422 being sent back to the client but you can still display the errors in your form since the form would get re-rendered by the HTML included in your stream.

I found myself doing this because I had a Stimulus controller taking advantage of the new “ValueChanged” callback to track the previous value of a form input. When I render :new due to an invalid form, the previous value was always lost because the entire page got reloaded but with the turbo_stream response I was able to track the previous value successfully.

Basically I wanted to prevent submitting a form if the input’s value wasn’t changed and the ValueChanged callback let me do that.

Something about this feels wrong though, or is this considered “the” way to do it for my Stimulus / Turbo use case?


I don’t know the project you are working on, that’s why I try to ask some questions, which may help you or not.

Is there a good reason to stop users from submitting a form on errors?
Is it for performance or usability reasons?
How do you want to make sure, that client-side validation and server-side validation are the same?

Some descriptions from my experience:

I had a lot of bad experiences with forms, which can’t be submitted because something is invalid. Mostly the submission was broken, because there was a white space, where it shouldn’t be, and the values were filled browser autocomplete data or some JS trigger wasn’t triggered and the user had to reload the page and reenter all the data he/she had entered before.

A simple submission and one place of validation (on the server) and not twice (client and server side) is in my opinion almost always the best experience for the user and developer. Sure there are reasons to do it another way, that’s why I’m asking the questions.


The form wouldn’t be stopped from being sent to the server in either case. In both cases the form submits to the server and you either do a full page reload and render a 422 or you return back the stream as a 200 (with or without failure).

In my case it’s specific to the use case I was doing. The full reload with a 422 doesn’t allow Stimulus to track its ValueChanged because the page gets reloaded in full.

But I think you can improve performance (and in turn usability) on all forms using this method. Today I noticed this might be what does too. If you goto and login with an invalid email it no longer does a full page reload and get a 422. You get back a turbo stream response with a 200 response.

If you try to login with JS disabled it sends you to a generic 422 error page without the form. I almost wonder if they have some type of fallback configured.