A simple controller that redirects to a path with a flash message works in a traditional rails app because usually there is a part in the app layout that renders flash messages.
However, when used with Turbo frames, flash messages are not shown during a frame controlled redirect as they are not part of the returning frame.
To fix this, I’ve wrapped my flash render view in a turbo frame called :notifications and in the action I use a turbo_stream.erb to send 2 streams down the client: one for the actions main response and a second one just to replace the :notifications frame, so the flash messages are shown.
Using this approach shows the flash message at the right time, but also repeats them on the next full refresh.
I was wondering if there is a “correct” way to deal with flash messages in Hotwire which I am completely missing out.
Thanks for sharing that. This is what I ended up doing, however I’m trying to find an elegant solution when the action needs to stream a partial to the client itself too. In cases like that, the format.turbo_stream can only send one turbo stream down from the action. To send more than one (the main one and the flash one) together, we need to move the payload to a turbo_stream.erb file and include the flash stream there every time.
However, when used with Turbo frames, flash messages are not shown during a frame controlled redirect as they are not part of the returning frame.
With a “frame controlled redirect”, are you saying the frame is redirected to additional content, but only display in the frame or it redirects the entire page?
So let’s say you want to redirect to a page after a certain operation is done in your action. In a normal Rails setup, you’d do that with a redirect_to. In such scenario, if you want to show a flash message, you then add a flash in the action too.
The suggested approach can either do the redirect OR the flash, not both. For flash to work with a stream, it would need to render a turbo_stream and a redirect would be considered double rendering.
To clarify, you want to be able to have a Rails controller respond with a turbo-stream (instead of a full-page redirect)? And in this scenario, you are getting the flash rendered within the turbo stream, and then on the next full page request. Is this correct?
I think that the stream approach is ok.
An alternative way could be to keep the frame and add a hidden div on the response (or add the same data to an existent element), managed by a Stimulus controller:
That’s what I was hoping for. When there is a .turbo_stream.erb file involved, one can use render turbo_stream, layout: 'foo' but I guess this is not possible when the rendering is something like this: render turbo_stream: turbo_stream.replace(...) as this doesn’t accept layouts.
The JS approach is interesting as it might even work with a full HTML render which is then inspected by a Stimulus controller for flash frames before it gets back to the original requesting frame.
A strean is a piece of html that have to be inserted/replaced asyncronally in an existing html. A “layout” have not much sense in this scenario. If you need to render more streams with a uniqe turbo_stream.xxxxx call you can always use a partial.
The suggestion in the article doesn’t work because it is for times when we don’t want to redirect the user. In this situation, what OP (and myself) try is to send a flash && redirect.
Sending the flash in a turbo_stream won’t cause a redirection. A form in a turbo_frame tag won’t send the flash to the view.
Evil Martian, with well known master Vladimir Dementyev, writings are always rich, in the following article, they talk about flash notification … they setup also a “flash layout for flash turbo_stream” and it’s interesting in my opinion.
In my case, I need to support both the normal requests and Turbo Stream request. But I really don’t want to change every places I am rendering turbo stream. I ended up creating a small around_action helper which allows me to check if I should broadcast the flash partial to the client.
Prerequisites:
a partial for rendering each flash message
a container for containing all flash messages
The around_action looks something like this
def broadcast_flash_message
return unless request.format.turbo_stream?
return if response.status == 301 || response.status == 302
flash.each do |key, message|
Turbo::StreamsChannel.broadcast_append_to(flash_message_container, target: flash_message_container, partial: "layouts/flash", locals: {message: message, type: key})
end
flash.clear
end
I’m not sure if it’s the correct way to do but it serves me well
is it good practices if I use turbo stream to sent an error notification?
I have done it problem is my turbo stream response have 200 OK instead 422 Error
and whenever I add the 422 Error my browser is not rendering the turbo stream response from server