Toast
Non-intrusive notifications that appear temporarily to provide feedback.
Quick Reference
Parts
| Partial | Description |
|---|---|
components/toaster |
Fixed container that holds all toasts |
components/toast |
Individual notification with auto-dismiss |
components/toast/title |
Toast title text |
components/toast/description |
Optional description text |
components/toast/action |
Optional action button/link |
Helper Methods
| Method | Description |
|---|---|
toast_flash_messages |
Render all flash messages as toasts |
toast |
Render a single toast with variant |
toast_success |
Convenience for success variant |
toast_error |
Convenience for error variant |
toast_warning |
Convenience for warning variant |
toast_info |
Convenience for info variant |
JavaScript API
| Method | Description |
|---|---|
Toast.show(title, options) |
Show a default toast |
Toast.success(title, options) |
Show a success toast |
Toast.error(title, options) |
Show an error toast |
Toast.warning(title, options) |
Show a warning toast |
Toast.info(title, options) |
Show an info toast |
Toast.dismiss(toastId) |
Dismiss a specific toast |
Toast.dismissAll() |
Dismiss all toasts |
Parameters
_toaster.html.erb
| Parameter | Type | Default | Description |
|---|---|---|---|
position |
Symbol | :bottom_right |
Position on screen |
css_classes |
String | "" |
Additional CSS classes |
**html_options |
Hash | {} |
HTML attributes |
Position Values:
:top_left,:top_center,:top_right:bottom_left,:bottom_center,:bottom_right
_toast.html.erb
| Parameter | Type | Default | Description |
|---|---|---|---|
variant |
Symbol | :default |
Toast variant |
title |
String | nil |
Toast title |
description |
String | nil |
Optional description |
duration |
Integer | 5000 |
Auto-dismiss in ms (0 = no auto-dismiss) |
dismissible |
Boolean | true |
Show close button |
css_classes |
String | "" |
Additional CSS classes |
**html_options |
Hash | {} |
HTML attributes |
Variant Values:
:default- Neutral style:success- Green, with check icon:info- Blue, with info icon:warning- Yellow/amber, with alert icon:error- Red, with X icon
toast/_action.html.erb
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
String | Required | Button/link text |
href |
String | nil |
Link URL (renders <a> if present) |
method |
Symbol | nil |
Turbo method (:delete, :patch, etc.) |
css_classes |
String | "" |
Additional CSS classes |
**html_options |
Hash | {} |
HTML attributes |
Data Attributes
Component Identifiers
| Attribute | Element | Description |
|---|---|---|
data-component="toaster" |
Container | Main container identifier |
data-component="toast" |
Toast | Individual toast identifier |
data-variant="success" |
Toast | Toast variant |
data-position="bottom-right" |
Toaster | Container position |
Stimulus Controllers
| Attribute | Description |
|---|---|
data-controller="toaster" |
Container controller (JS API) |
data-controller="toast" |
Individual toast controller |
State Attributes
| Attribute | Description |
|---|---|
data-state="entering" |
Toast is animating in |
data-state="visible" |
Toast is visible |
data-state="exiting" |
Toast is animating out |
Setup
Add the toaster container to your application layout:
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<%= yield %>
<%= render "components/toaster", position: :bottom_right %>
</body>
</html>
Basic Usage
Flash Messages (Server-Side)
The most common use case is rendering Rails flash messages:
<%= render "components/toaster", position: :bottom_right do %>
<%= toast_flash_messages %>
<% end %>
In your controller:
class UsersController < ApplicationController
def update
if @user.update(user_params)
flash[:success] = "Profile updated successfully!"
redirect_to @user
else
flash[:error] = "Unable to save changes."
render :edit, status: :unprocessable_entity
end
end
end
Single Toast (Server-Side)
<%= toast :success, "Profile updated!" %>
<%= toast :error, "Save failed", description: "Please check your connection." %>
<%= toast :info, "New version available", duration: 10000 %>
Toast with Action
<%= toast :info, "New version available" do %>
<%= render "components/toast/action", label: "Refresh", href: root_path %>
<% end %>
JavaScript API
// Basic toasts
Toast.success("Profile saved!")
Toast.error("Something went wrong")
Toast.warning("Session expiring soon")
Toast.info("New notification")
// With options
Toast.success("File uploaded", {
description: "Your file has been processed.",
duration: 8000
})
// With action
Toast.info("New comment", {
description: "John replied to your post.",
action: { label: "View" }
})
// No auto-dismiss
Toast.warning("Important notice", { duration: 0 })
// Dismiss programmatically
const toastId = Toast.success("Saved!")
Toast.dismiss(toastId)
// Dismiss all
Toast.dismissAll()
Examples
Toaster Positions
<%= render "components/toaster", position: :top_right do %>
<%= toast_flash_messages %>
<% end %>
<%= render "components/toaster", position: :bottom_center do %>
<%= toast_flash_messages %>
<% end %>
<%= render "components/toaster", position: :top_left do %>
<%= toast_flash_messages %>
<% end %>
All Variants
<%= toast :default, "Default notification" %>
<%= toast :success, "Success!", description: "Your changes have been saved." %>
<%= toast :info, "Did you know?", description: "You can customize your dashboard." %>
<%= toast :warning, "Warning", description: "Your session will expire in 5 minutes." %>
<%= toast :error, "Error", description: "Unable to connect to server." %>
With Custom Duration
<%= toast :success, "Saved!", duration: 3000 %>
<%= toast :info, "Please read this carefully", duration: 10000 %>
<%= toast :warning, "Action required", duration: 0 %>
Non-Dismissible
<%= toast :info, "Processing...", dismissible: false, duration: 0 %>
Action Buttons
<%= toast :info, "New message" do %>
<%= render "components/toast/action", label: "View", href: messages_path %>
<% end %>
<%= toast :warning, "Item pending deletion" do %>
<%= render "components/toast/action",
label: "Undo",
href: restore_item_path(@item),
method: :patch %>
<% end %>
<%= toast :success, "Form submitted" do %>
<%= render "components/toast/action",
label: "View Results",
href: results_path,
data: { turbo_frame: "results" } %>
<% end %>
Real-World Patterns
Form Submission Feedback
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
@post = Post.new(post_params)
if @post.save
flash[:success] = "Post created successfully!"
redirect_to @post
else
flash.now[:error] = "Unable to create post. Please fix the errors below."
render :new, status: :unprocessable_entity
end
end
end
Turbo Stream Toasts
For Turbo Stream responses, append toasts dynamically:
<%= turbo_stream.append "toaster" do %>
<%= toast :success, "Post created!", description: "Your post is now live." %>
<% end %>
JavaScript Event Handling
<div data-controller="notifications">
<%= form_with model: @user,
data: { action: "turbo:submit-end->notifications#showResult" } do |f| %>
<% end %>
</div>
// notifications_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
showResult(event) {
if (event.detail.success) {
Toast.success("Changes saved!")
} else {
Toast.error("Unable to save", {
description: "Please check the form for errors."
})
}
}
}
Async Operation Feedback
async function uploadFile(file) {
const loadingId = Toast.info("Uploading...", {
description: file.name,
duration: 0,
dismissible: false
})
try {
await performUpload(file)
Toast.dismiss(loadingId)
Toast.success("Upload complete!", {
description: `${file.name} has been uploaded.`
})
} catch (error) {
Toast.dismiss(loadingId)
Toast.error("Upload failed", {
description: error.message
})
}
}
Flash Type Mapping
The helper automatically maps Rails flash types to toast variants:
| Flash Type | Toast Variant |
|---|---|
:notice |
:success |
:success |
:success |
:alert |
:error |
:error |
:error |
:warning |
:warning |
:warn |
:warning |
:info |
:info |
Excluding Flash Types
<%= toast_flash_messages exclude: [:notice] %>
Behavior
Auto-Dismiss Timer
- Default duration: 5000ms (5 seconds)
- Timer pauses on hover
- Timer resumes when mouse leaves
- Set
duration: 0to disable auto-dismiss
Animation
- Enters from the edge based on position
- Fades out when dismissed
- Smooth transitions for position changes
Stacking
- New toasts stack based on position (bottom positions stack upward)
- Maximum 5 visible toasts by default
- Oldest toasts are dismissed when limit is reached
Keyboard Interaction
| Key | Action |
|---|---|
Tab |
Move focus between toast elements |
Escape |
Dismiss focused toast |
Enter / Space |
Activate action button |
Theme Variables
The toast component uses these CSS variables:
/* Base */
var(--popover)
var(--popover-foreground)
var(--border)
var(--foreground)
var(--muted-foreground)
/* Success variant */
var(--success)
var(--success-foreground)
/* Info variant */
var(--info) /* Falls back to --primary */
var(--info-foreground) /* Falls back to --primary-foreground */
/* Warning variant */
var(--warning)
var(--warning-foreground)
/* Error variant */
var(--destructive)
var(--destructive-foreground)
/* Interactive */
var(--accent)
var(--accent-foreground)
var(--ring)
Accessibility
- Container has
role="region"witharia-label="Notifications" - Container has
aria-live="polite"for screen reader announcements - Individual toasts have
role="alert" - Close button has accessible label
- Focus management for keyboard users
- Pause on hover prevents dismissing while reading
- Action buttons are keyboard accessible
File Structure
app/views/components/
├── _toaster.html.erb
├── _toast.html.erb
└── toast/
├── _title.html.erb
├── _description.html.erb
└── _action.html.erb
app/assets/stylesheets/toast.css
app/javascript/controllers/toaster_controller.js
app/javascript/controllers/toast_controller.js
app/helpers/maquina_components/toast_helper.rb
docs/toast.md
Differences from Alert
| Feature | Toast | Alert |
|---|---|---|
| Positioning | Fixed, overlays content | Inline, part of flow |
| Duration | Temporary, auto-dismiss | Persistent |
| Stacking | Multiple can stack | Single at a time |
| Use Case | Feedback, notifications | Important messages, errors |
| JavaScript | Full API for dynamic creation | Static only |