Toast

A succinct message that is displayed temporarily.

Anatomy

Toast do |t|
t.content do
t.title
t.description
end
end

API Reference

Toast

NameTypeDefault
variant:symbol:default
Visual style of the toast. Options: :default | :destructive
side:symbol:top
Controls slide in/out animation direction. Set this to bottom if ToastContainer is set to one of :bottom_center | :bottom_left | :bottom_right.

Options: :top | :bottom
duration:number5000
The time in milliseconds that should elapse before automatically closing each toast. Setting it to 0 will disable the auto-close feature.
attributeskeyword arguments

Toast#content

NameType
attributeskeyword arguments

Toast#title

NameType
attributeskeyword arguments

Toast#description

NameType
attributeskeyword arguments

Toast#action

NameTypeDefault
as_child:booleanfalse
When true, merges toast action styling with a custom element passed as block content instead of rendering a default button.
attributeskeyword arguments

Toast#action_to

Rails button_to helper styled as a toast action button.

NameTypeDefault
namestringnil
The button text or content to display.
optionsstring | hashnil
URL or options hash for the form submission target.
html_optionshashnil
Additional HTML attributes for the form and button elements.

ToastContainer

NameTypeDefault
side:symbol:top_center
Positioning of the toast container on screen. Options: :top_center | :top_left | :top_right | :bottom_center | :bottom_left | :bottom_right
attributeskeyword arguments

Keyboard Interactions

KeyDescription
TabMoves focus to the next focusable element.
Shift+TabMoves focus to the previous focusable element.
EnterWhen focus is on the close button, closes the toast.
SpaceWhen focus is on the close button, closes the toast.
EscWhen focus is on a Toast, closes the toast.

Examples

Toast Container With Flash

# some_controller.rb
def create
redirect_to path,
notice: {
title: "Uh oh! Something went wrong.",
description: "There was a problem with your request."
})
end
# layouts/application.html.erb
<%= render ToastContainer.new(id: "toast-container") do %>
<% flash.each do |flash_type, flash_content| %>
<%= render Toast.new(variant: flash_type == "notice" ? :default : :destructive) do |t| %>
<%= t.content do %>
<% if flash_content.is_a?(Hash) %>
<% flash_content = flash_content.transform_keys(&:to_sym) %>
<%= t.title { flash_content["title"] } %>
<%= t.description { flash_content["description"] } %>
<% else %>
<%= t.title { flash_content } %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<form action="/examples/toast" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="JX_EyEw3Rn4YhvlUHlkKiCQkIFTWCO1KykfmcDOdtVSGsG0-Y6TR8VVruf1jvuWDPg6TxWlpJMw4imP3oHa0wA" autocomplete="off" /> <button type="submit" data-shadcn-phlexcomponents="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3">Show toast</button>
</form>

Toast Container With JS

<%= render Button.new(id: "show-toast", variant: :outline) { "Show toast" } %>
<button type="button" id="show-toast" data-shadcn-phlexcomponents="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3">Show toast</button>
// application.js
const button = document.querySelector('#show-toast')
const toastContainer =
window.Stimulus.getControllerForElementAndIdentifier(
document.querySelector('#toast-container'),
'toast-container',
)
button.addEventListener('click', () => {
toastContainer.add({
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
})
})

Toast Container With JS (Custom HTML)

<%= render Button.new(id: "show-toast-custom", variant: :outline) { "Show toast" } %>
<button type="button" id="show-toast-custom" data-shadcn-phlexcomponents="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3">Show toast</button>
// application.js
const button = document.querySelector('#show-toast-custom')
const toastContainer =
window.Stimulus.getControllerForElementAndIdentifier(
document.querySelector('#toast-container'),
'toast-container',
)
button.addEventListener('click', () => {
toastContainer.addToast({
variant: 'destructive',
title: '<div class="text-lg">This is a custom title</div>',
description:
'<ul class="list-disc ml-4"><li>item 1</li><li>item 2</li><ul>',
action:
'<button data-controller="try-again" data-action="try-again#tryAgain">Try again</button>',
duration: 10000,
})
})

Toast Container With Turbo Stream

# some_controller.rb
def create
respond_to do |format|
format.turbo_stream {
flash.now[:notice] = {
title: "Uh oh! Something went wrong.",
description: "There was a problem with your request."
}
}
end
end
# some_controller/create.turbo_stream.erb
<%= turbo_stream.prepend "toast-container", partial: "layouts/flash" %>
# layouts/application.html.erb
<%= render ToastContainer.new(id:"toast-container") do %>
<%= render "layouts/flash" %>
<% end %>
# layouts/flash.html.erb
<% flash.each do |flash_type, flash_content| %>
<%= render Toast.new(variant: flash_type == "notice" ? :default : :destructive) do |t| %>
<%= t.content do %>
<% if flash_content.is_a?(Hash) %>
<% flash_content = flash_content.transform_keys(&:to_sym) %>
<%= t.title { flash_content[:title] } %>
<%= t.description { flash_content[:description] } %>
<% else %>
<%= t.title { flash_content } %>
<% end %>
<% end %>
<% end %>
<% end %>
<form action="/examples/toast_turbo_stream" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="g7QkNxczFUt8xMEoE6jUyHkuNY6XA5et6uB5Z4BUnFpvi2V_HJzuYbdwE6HbJFQexkjuUodOPkYtlvBdn7ZoXA" autocomplete="off" /> <button type="submit" data-shadcn-phlexcomponents="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3">Show toast</button>
</form>