Starting Point
v0.25.0
Start typing to search...
Go to Page
escClose

Toast

The toast shows a brief notification that stacks in a corner and dismisses itself, like "Changes saved" after a form submit. It is created from JavaScript rather than authored markup; for a message that must interrupt and be acknowledged, use a dialog instead.

<button
  class="btn btn-outline"
  onclick="sp.toast('Event has been created', { description: 'Sunday, December 03, 2023 at 9:00 AM', action: { label: 'Undo', onClick: () => console.log('Undo') } })"
>
  Show Toast
</button>

Usage

Call sp.toast() with a message and an optional configuration object, and everything is created for you: toasts stack with the newest in front, and hovering the stack expands it. The rendered elements carry these classes:

ClassDescription
toasterThe stacking container, one per position
toastEach notification panel
toast-iconThe type icon
toast-title / toast-descriptionThe text slots
toast-actionThe action button wrapper
toast-closeThe dismiss button, shown on hover
sp.toast("Event has been created");

Examples

Types

Use the shorthand methods for a leading status icon, or pass the type option.

<div class="flex flex-wrap justify-center gap-2">
  <button class="btn btn-outline" onclick="sp.toast('Event has been created')">
    Default
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast.success('Event has been created')"
  >
    Success
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast.info('Be at the area 10 minutes before the event time')"
  >
    Info
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast.warning('Event start time cannot be earlier than 8am')"
  >
    Warning
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast.error('Event has not been created')"
  >
    Error
  </button>
</div>

Description

Add a secondary line of text with the description option.

<button
  class="btn btn-outline"
  onclick="sp.toast('Profile updated', { description: 'Your changes have been saved and are now live.' })"
>
  With Description
</button>

Type with description

A status icon next to a title and description.

<button
  class="btn btn-outline"
  onclick="sp.toast.success('Event has been created', { description: 'Sunday, December 03, 2023 at 9:00 AM' })"
>
  Success with Description
</button>

Action

Add a clickable action button; choosing it also dismisses the toast.

<button
  class="btn btn-outline"
  onclick="sp.toast('File deleted', { description: 'report-2024.pdf was moved to trash.', action: { label: 'Undo', onClick: () => sp.toast.success('File restored') } })"
>
  With Action
</button>

Loading and update

sp.toast.loading() shows a spinner and stays put; call update() on the returned instance to swap it to a result state.

<button
  class="btn btn-outline"
  onclick="(function(){ var t = sp.toast.loading('Loading data...'); setTimeout(function(){ t.update({ title: 'Data loaded successfully', type: 'success' }) }, 2000) })()"
>
  Loading then Success
</button>
const t = sp.toast.loading("Creating post...");
 
const res = await fetch("/api/posts", { method: "POST", body });
 
if (res.ok) {
  t.update({ title: "Post created", type: "success" });
} else {
  t.update({ title: "Failed to create post", type: "error" });
}

Promise

Wrap a promise with sp.toast.promise(): it shows a loading toast, then swaps to the success or error state when the promise settles. The messages can be strings or functions of the resolved value.

<button
  class="btn btn-outline"
  onclick="sp.toast.promise(new Promise(function (resolve) { setTimeout(resolve, 2000) }), { loading: 'Uploading file...', success: 'File uploaded', error: 'Upload failed' })"
>
  Upload
</button>
sp.toast.promise(fetch("/api/posts", { method: "POST", body }), {
  loading: "Creating post...",
  success: "Post created",
  error: (err) => `Failed: ${err.message}`,
});

Duration

Control how long the toast stays (milliseconds, default 4000); 0 keeps it until dismissed.

<div class="flex flex-wrap justify-center gap-2">
  <button
    class="btn btn-outline"
    onclick="sp.toast('Gone in 2 seconds', { duration: 2000 })"
  >
    Short (2s)
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Staying for 10 seconds', { duration: 10000 })"
  >
    Long (10s)
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('I won\'t go away on my own', { duration: 0 })"
  >
    Persistent
  </button>
</div>

Position

Place the stack in any corner or centered at the top or bottom; default is bottom-right.

<div class="flex flex-wrap justify-center gap-2">
  <button
    class="btn btn-outline"
    onclick="sp.toast('Top left', { position: 'top-left' })"
  >
    Top Left
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Top center', { position: 'top-center' })"
  >
    Top Center
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Top right', { position: 'top-right' })"
  >
    Top Right
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Bottom left', { position: 'bottom-left' })"
  >
    Bottom Left
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Bottom center', { position: 'bottom-center' })"
  >
    Bottom Center
  </button>
  <button
    class="btn btn-outline"
    onclick="sp.toast('Bottom right', { position: 'bottom-right' })"
  >
    Bottom Right
  </button>
</div>

Non-dismissible

Set dismissible: false to hide the close button; the toast leaves only when its duration expires.

<button
  class="btn btn-outline"
  onclick="sp.toast.info('Processing your request...', { dismissible: false, duration: 3000 })"
>
  Non-dismissible
</button>

Declarative toasts

Add data-sp-toast to any element to fire it as a toast and remove the element, with a plain string or a JSON config. New elements are picked up as they enter the DOM, so server-rendered redirects can queue flash messages with no JavaScript of their own.

<div data-sp-toast="Changes saved"></div>
 
<div
  data-sp-toast='{"title":"Post created","type":"success","description":"Your post is now live."}'
></div>

For example, in Laravel Blade:

@if (session('toast'))
    <div data-sp-toast='@json(session("toast"))'></div>
@endif
return redirect()->back()->with('toast', [
    'title' => 'Post created',
    'type' => 'success',
]);

Toasts queued this way survive being rendered by React, Livewire, HTMX, or anything else that inserts nodes after page load. On Back/Forward navigation, cached flash markup does not re-fire.

Options

Pass options as the second argument to sp.toast().

OptionTypeDefaultDescription
typeStringdefaultsuccess, error, warning, info, or loading
descriptionStringSecondary text under the title
durationNumber4000Milliseconds before auto-dismiss; 0 keeps the toast until dismissed
actionObject{ label, onClick } rendered as a button; clicking also dismisses
positionStringbottom-righttop-left, top-center, top-right, bottom-left, bottom-center, bottom-right
dismissibleBooleantrueShow the close button

JavaScript

Events

Lifecycle events fire on each toast element and bubble to document.

EventWhen
sp-showEntering
sp-shownShown and settled
sp-hideLeaving
sp-hiddenRemoved

Methods

sp.toast() returns a toast instance; the module also exposes global helpers.

MethodDescription
sp.toast(title, options)Show a toast, returns { id, update, dismiss }
sp.toast.success(title, options)Shorthand for each type (success, error, warning, info, loading)
instance.update(options)Swap title, type, description, and duration in place
instance.dismiss()Dismiss this toast
sp.toast.dismiss(id)Dismiss a toast by id, or every toast when called without one
sp.toast.promise(promise, messages)Show a loading toast that settles with the promise