Toast

A succinct message that is displayed temporarily.

Stimulus Controller

Installation

Add the component to your project:

rails generate shadcn:component toast

Usage

Toasts are typically triggered by user actions and rendered dynamically. The ToastViewportComponent provides a container positioned at the bottom-right of the screen (or top on mobile).

<%= render Shadcn::ToastViewportComponent.new do %>
  <%= render Shadcn::ToastComponent.new do |toast| %>
    <% toast.with_title { "Scheduled" } %>
    <% toast.with_description { "Your message has been scheduled." } %>
  <% end %>
<% end %>

Examples

Simple

A minimal toast with just a description.

<%= render Shadcn::ToastComponent.new do |toast| %>
  <% toast.with_description { "Your message has been sent." } %>
<% end %>

With Title

Include both a title and description for more context.

<%= render Shadcn::ToastComponent.new do |toast| %>
  <% toast.with_title { "Scheduled: Catch up" } %>
  <% toast.with_description { "Friday, February 10, 2023 at 5:57 PM" } %>
<% end %>

With Action

Add an action button to allow users to respond to the notification. The alt_text parameter is required for accessibility.

<%= render Shadcn::ToastComponent.new do |toast| %>
  <% toast.with_title { "Scheduled" } %>
  <% toast.with_description { "Your message has been scheduled." } %>
  <% toast.with_action(alt_text: "Undo") do %>
    Undo
  <% end %>
<% end %>

Destructive

Use the destructive variant for error messages or critical notifications.

<%= render Shadcn::ToastComponent.new(variant: :destructive) do |toast| %>
  <% toast.with_title { "Uh oh! Something went wrong." } %>
  <% toast.with_description { "There was a problem with your request." } %>
  <% toast.with_action(alt_text: "Try again") do %>
    Try again
  <% end %>
<% end %>

Custom Duration

Control how long the toast is displayed before auto-dismissing. Duration is specified in milliseconds.

<%= render Shadcn::ToastComponent.new(duration: 3000) do |toast| %>
  <% toast.with_description { "This will close in 3 seconds." } %>
<% end %>

No Auto-dismiss

Set duration: 0 to prevent automatic dismissal. The user must manually close the toast.

<%= render Shadcn::ToastComponent.new(duration: 0) do |toast| %>
  <% toast.with_title { "Important" } %>
  <% toast.with_description { "This toast will stay until manually dismissed." } %>
<% end %>

API Reference

ToastComponent

API Reference

Prop Type Default Description
variant Symbol :default Toast style variant (:default, :destructive)
duration Integer 5000 Auto-dismiss duration in milliseconds (0 for no auto-dismiss)
open Boolean true Whether the toast is visible
class_name String nil Additional CSS classes to apply

ToastComponent Slots

Slot Props Description
with_title - Toast title (bold heading)
with_description - Toast description text
with_action alt_text: (required) Action button (requires alt_text for accessibility)
with_close - Custom close button (default provided)

ToastViewportComponent

Container component for toasts, positioned at bottom-right on desktop and top on mobile.

API Reference

Prop Type Default Description
class_name String nil Additional CSS classes to apply

Stimulus Controller

The Toast component uses a Stimulus controller (shadcn--toast) for auto-dismiss functionality and close button interaction.

Values

Value Type Default Description
duration Number 5000 Auto-dismiss time in milliseconds
open Boolean true Whether toast is visible

Actions

Action Description
close Manually close the toast
pause Pause auto-dismiss timer (useful on hover)
resume Resume auto-dismiss timer

Events

Event Description
shadcn--toast:closed Dispatched when toast is closed and removed from DOM

Integration Example

Here's an example of dynamically adding toasts using Turbo Streams or JavaScript:

// Add a toast dynamically
function addToast(title, description, variant = 'default') {
  const viewport = document.querySelector('[data-shadcn--toaster-target="viewport"]');
  const toast = document.createElement('li');

  toast.setAttribute('data-controller', 'shadcn--toast');
  toast.setAttribute('data-shadcn--toast-duration-value', '5000');
  toast.setAttribute('data-shadcn--toast-open-value', 'true');
  toast.setAttribute('data-state', 'open');
  toast.setAttribute('role', 'status');
  toast.setAttribute('aria-live', 'polite');

  const variantClasses = variant === 'destructive'
    ? 'destructive group border-destructive bg-destructive text-destructive-foreground'
    : 'border bg-background text-foreground';

  toast.className = `group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all ${variantClasses}`;

  toast.innerHTML = `
    <div class="grid gap-1">
      ${title ? `<div class="text-sm font-semibold [&+div]:text-xs">${title}</div>` : ''}
      <div class="text-sm opacity-90">${description}</div>
    </div>
    <button type="button" class="absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100" data-action="click->shadcn--toast#close" aria-label="Close">
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" class="h-4 w-4">
        <path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </button>
  `;

  viewport.appendChild(toast);
}

Accessibility

  • Uses role="status" for proper screen reader announcement
  • Has aria-live="polite" to announce changes without interrupting
  • Close button has aria-label="Close" for screen readers
  • Action buttons require alt_text parameter for aria-label
  • Auto-dismiss timer can be paused on hover for users who need more time to read
  • All interactive elements are keyboard accessible
  • Color is not the only indicator - text content provides meaning