Toastr Flash Messages with Turbo, Stimulus & Ruby on Rails
The Toastr plugin is one of my favourite pre-built plugins for displaying alerts for users. I often use Toastr alerts alongside usual flash messages (ex: Bootstrap alerts) on single page applications or remote forms.
Since Rails 7, I started coupling Toastr with Turbo and Stimulus. This article explains how to show Toastr alerts using Turbo and Stimulus in a Rails project. I assume you have already installed the Toastr plugin either through yarn add toastr
or pinning it through import maps.
Background
The alert will come as part of the form submission. We will send a turbo-stream including the flash message to the client-side. The flash message will point to a Stimulus controller (FlashController
) which will in turn fire the Toastr flash message and also auto traditional flash message.
Instruction
I structure my application using Trailblazer Cells, Operations and Reform. This is my Flash component. Think of these as the flash partial and a corresponding helper module.
module JDeen::Cell::Marketing
class Flash < Trailblazer::Cell; end
end
#flash-container
- model.each do |key, value|
.alert.alert-dismissible.fade.in[
class="alert-#{key}"
role="alert"
data-controller="flash"
data-flash-type=key
data-flash-message=value
]
button.close aria-label="Close" data-dismiss="alert" type="button"
span aria-hidden="true" ⨯
= value
In the Slim file, for each alert, we have defined data-controller
, data-flash-type
and data-flash-message
attributes. The data-controller
is to specify the Stimulus controller and the others are there to easily extract flash details.
This is the Stimulus controller that will handle the flash messages. I am going to auto dismiss the flash messages in 5 seconds and also create Toastr alerts for each flash message.
// app/javascripts/controllers/flash_controller.js
import {Controller} from "@hotwired/stimulus";
import toastr from "toastr";
export default class extends Controller {
connect() {
let type = this.element.dataset.flashType;
let message = this.element.dataset.flashMessage;
// Auto remove the flash messages
setTimeout(() => {
this.element.remove();
}, 5000);
// Create Tostr flash messages
toastr[type](message);
}
}
We now have what it takes to show the flash messages. How do we deliver the flash messages to the client side? In the old days, we would have used a JSON response or JS responses to achieve our goals. But today we can entirely rely on turbo streams.
This is a simplified controller for posting comments for my blog posts.
# app/controllers/marketing/posts/comments_controller.rb
class Marketing::Posts::CommentsController < ApplicationController
# ...
def create
respond_to do |format|
flash.now[:success] = "Thank you #{context[:model].user.name} for your comment!"
format.turbo_stream do
render turbo_stream: [
# I am using Trailblazer on top of Ruby on Rails. This is similar to
# rendering a partial. You may use `partial: 'layouts/frontend/flash'`
# kind of approach to render the partial.
turbo_stream.replace(
"flash-container",
html: cell(JDeen::Cell::Marketing::Flash, flash)
),
# Turbo frame to render a new form
# Turbo frame to append the new comment
]
end
end
# ...
end
We are rendering a turbo stream. That is basically a set of turbo frames. Among many other activities, we may need to do, we are particularly interested in replacing the contents of the flash component. To replace components we use turbo_stream.replace()
.
Note: Turbo by Hotwire has many actions in addition to replace
used above. They are append
, prepend
, replace
, update
, remove
, before
and after
. More details about Turbo Stream Actions
That is it, now you will have a form submission that will not only display a traditional flash but also fire a Toastr alert.
About the Author
Ziyan Junaideen -
Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.