Toast
UI::ToastA stacked, auto-dismissing message, pushed from the client or a Turbo Stream.
# VelvetKit
## Toast (`UI::Toast`)
A stacked, auto-dismissing message, pushed from the client or a Turbo Stream.
Add it: `bin/rails generate velvetkit:add toast`
Also adds: `icon`, `spinner`, `button`
Stimulus: `ui--toast`, `ui--toast-viewport`
### Arguments
```ruby
UI::Toast.new(variant: :default, title: nil, description: nil, duration: 5, dismissible: true, icon: true, class: nil, **html_attrs)
```
Slots: `with_action`
### Notes
UI::Toast — a single dismissible, auto-expiring message.
A toast has nowhere to appear on its own: render a UI::Toast::Viewport once
per page (e.g. in the layout), then push toasts into it either from the
server, by appending one in a Turbo Stream response —
turbo_stream.append(UI::Toast::Viewport::ID, render(UI::Toast.new(variant: :success, title: "Saved")))
— or from the client, with the `toast()` helper exported by the
toast-viewport controller.
### Example: All Variants
```erb
<div class="flex flex-col gap-4 max-w-sm">
<%= render(UI::Toast.new(variant: :default, title: "Default", description: "Heads up, something happened.")) %>
<%= render(UI::Toast.new(variant: :success, title: "Success", description: "Your changes have been saved.")) %>
<%= render(UI::Toast.new(variant: :destructive, title: "Destructive", description: "There was a problem with your request.")) %>
<%= render(UI::Toast.new(variant: :warning, title: "Warning", description: "This action cannot be undone.")) %>
<%= render(UI::Toast.new(variant: :info, title: "Info", description: "Here's some helpful information.")) %>
</div>
```
### Example: With Action
```erb
<div class="max-w-sm">
<%= render(UI::Toast.new(variant: :default, title: "Update available", duration: 0)) do |toast| %>
<% toast.with_action do %>
<%= render(UI::Button.new(size: :sm)) { "Reload" } %>
<% end %>
<% end %>
</div>
```
### Example: Live Viewport
```erb
<div class="flex flex-wrap gap-2">
<%
demos = {
default: ["Notification", "Heads up, something happened."],
success: ["Saved", "Your changes have been saved."],
destructive: ["Something broke", "There was a problem with your request."],
warning: ["Careful", "This action cannot be undone."],
info: ["FYI", "Here's some helpful information."]
}
%>
<% demos.each do |variant, (title, description)| %>
<% payload = { title: title, description: description, variant: variant }.to_json %>
<%= render(UI::Button.new(variant: :outline, size: :sm,
onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{payload} }))")) { title } %>
<% end %>
<% persistent = { title: "Stays until dismissed", description: "duration: 0 skips the auto-dismiss timer.", variant: :default, duration: 0 }.to_json %>
<%= render(UI::Button.new(variant: :outline, size: :sm,
onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{persistent} }))")) { "Persistent" } %>
</div>
<%= render(UI::Toast::Viewport.new) %>
```
bin/rails generate velvetkit:add toast
- Stimulus
- ui--toast ui--toast-viewport
- Installs
- Nothing
Examples
All Variants
variants.html.erb
<div class="flex flex-col gap-4 max-w-sm">
<%= render(UI::Toast.new(variant: :default, title: "Default", description: "Heads up, something happened.")) %>
<%= render(UI::Toast.new(variant: :success, title: "Success", description: "Your changes have been saved.")) %>
<%= render(UI::Toast.new(variant: :destructive, title: "Destructive", description: "There was a problem with your request.")) %>
<%= render(UI::Toast.new(variant: :warning, title: "Warning", description: "This action cannot be undone.")) %>
<%= render(UI::Toast.new(variant: :info, title: "Info", description: "Here's some helpful information.")) %>
</div>
With Action
with_action.html.erb
<div class="max-w-sm">
<%= render(UI::Toast.new(variant: :default, title: "Update available", duration: 0)) do |toast| %>
<% toast.with_action do %>
<%= render(UI::Button.new(size: :sm)) { "Reload" } %>
<% end %>
<% end %>
</div>
Live Viewport
live_viewport.html.erb
<div class="flex flex-wrap gap-2">
<%
demos = {
default: ["Notification", "Heads up, something happened."],
success: ["Saved", "Your changes have been saved."],
destructive: ["Something broke", "There was a problem with your request."],
warning: ["Careful", "This action cannot be undone."],
info: ["FYI", "Here's some helpful information."]
}
%>
<% demos.each do |variant, (title, description)| %>
<% payload = { title: title, description: description, variant: variant }.to_json %>
<%= render(UI::Button.new(variant: :outline, size: :sm,
onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{payload} }))")) { title } %>
<% end %>
<% persistent = { title: "Stays until dismissed", description: "duration: 0 skips the auto-dismiss timer.", variant: :default, duration: 0 }.to_json %>
<%= render(UI::Button.new(variant: :outline, size: :sm,
onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{persistent} }))")) { "Persistent" } %>
</div>
<%= render(UI::Toast::Viewport.new) %>
API
Arguments
UI::Toast.new(variant: :default, title: nil, description: nil, duration: 5, dismissible: true, icon: true, class: nil, **html_attrs)
Slots:
with_action
Notes
UI::Toast — a single dismissible, auto-expiring message.
A toast has nowhere to appear on its own: render a UI::Toast::Viewport once
per page (e.g. in the layout), then push toasts into it either from the
server, by appending one in a Turbo Stream response —
turbo_stream.append(UI::Toast::Viewport::ID, render(UI::Toast.new(variant: :success, title: "Saved")))
— or from the client, with the `toast()` helper exported by the
toast-viewport controller.