Browse Components

Toast

Brief feedback that keeps people in their flow.

Basic

A short confirmation stays visible until dismissed. Showing it never moves keyboard focus.

View Code
HTML
<button
  class="btn secondary"
  type="button"
  data-toast-target="notifications"
  data-toast-title="Changes saved"
  data-toast-description="Your preferences are up to date."
  hidden
>
  Show Toast
</button>
<div
  class="toasts"
  id="notifications"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<noscript>
  <p>
    Notifications need JavaScript. Keep important results visible in the page as well.
  </p>
</noscript>

Status messages

Use clear wording alongside the status icon. Errors remain visible; routine messages use polite announcements.

View Code
HTML
<div class="cluster">
  <button
    class="btn secondary"
    type="button"
    data-toast-target="status-toasts"
    data-toast-title="Changes saved"
    data-toast-description="Your preferences are up to date."
    data-toast-tone="success"
    hidden
  >
    Success
  </button>
  <button
    class="btn secondary"
    type="button"
    data-toast-target="status-toasts"
    data-toast-title="A new version is available"
    data-toast-description="Refresh when you are ready."
    data-toast-tone="info"
    hidden
  >
    Information
  </button>
  <button
    class="btn secondary"
    type="button"
    data-toast-target="status-toasts"
    data-toast-title="Storage is nearly full"
    data-toast-description="Review your files before the next upload."
    data-toast-tone="warning"
    hidden
  >
    Warning
  </button>
  <button
    class="btn secondary"
    type="button"
    data-toast-target="status-toasts"
    data-toast-title="Changes could not be saved"
    data-toast-description="Check your connection and try again."
    data-toast-tone="error"
    hidden
  >
    Error
  </button>
</div>
<div
  class="toasts"
  id="status-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Auto-dismiss

This message closes after six seconds. Hover, focus a control, or switch browser tabs to pause the countdown.

View Code
HTML
<button
  class="btn secondary"
  type="button"
  data-toast-target="timed-toasts"
  data-toast-title="Link copied"
  data-toast-description="You can paste it into your message."
  data-toast-duration="6000"
  hidden
>
  Show Timed Toast
</button>
<div
  class="toasts"
  id="timed-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

With an action

Offer a clear next step without a time limit. This example changes only the demo message; it does not delete or restore real data.

View Code
HTML
<button class="btn secondary" type="button" id="action-demo" hidden>
  Try Undo
</button>
<div
  class="toasts"
  id="action-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<script>
  addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('action-demo');
  const toast = document.getElementById('action-toasts').toast;
  button.hidden = false;
  button.addEventListener('click', () => toast.show({
    title: 'Demo item removed',
    description: 'You can restore it with Undo.',
    action: { label: 'Undo', onClick: ({ id }) => toast.update(id, {
      title: 'Demo item restored', description: 'Nothing was saved or deleted.',
      tone: 'success', action: null
    }) }
  }));
});
</script>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Update a message

Use the returned id to update a single message as work progresses. These buttons simulate a result without making a request.

View Code
HTML
<div class="cluster">
  <button class="btn secondary" id="start-demo" type="button" hidden>
    Start Demo
  </button>
  <button class="btn secondary" id="finish-demo" type="button" disabled hidden>
    Complete Demo
  </button>
</div>
<div
  class="toasts"
  id="update-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<script>
  addEventListener('DOMContentLoaded', () => {
  const root = document.getElementById('update-toasts');
  const start = document.getElementById('start-demo');
  const finish = document.getElementById('finish-demo');
  let id;
  start.hidden = finish.hidden = false;
  start.addEventListener('click', () => {
    id = root.toast.show({ title: 'Preparing your export', description: 'Demo only. No files are being created.', tone: 'loading' });
    start.disabled = true; finish.disabled = false;
  });
  finish.addEventListener('click', () => {
    root.toast.update(id, { title: 'Demo export ready', description: 'The existing notification was updated.', tone: 'success' });
    finish.disabled = true;
  });
  root.addEventListener('toast-dismiss', () => { start.disabled = false; finish.disabled = true; });
});
</script>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Several messages

Click repeatedly to see three messages at once. Further messages wait until a visible message is dismissed.

View Code
HTML
<button
  class="btn secondary"
  type="button"
  data-toast-target="stack-toasts"
  data-toast-title="New activity"
  data-toast-description="A teammate left a comment on your draft."
  hidden
>
  Add Notification
</button>
<div
  class="toasts"
  id="stack-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Placement

Try all six positions. Start and end follow the reading direction; centered toasts stay centered. Each choice clears the previous preview.

View Code
HTML
<div class="cluster">
  <button class="btn secondary" type="button" data-demo-position="top-start" hidden>
    Top Start
  </button>
  <button class="btn secondary" type="button" data-demo-position="top-center" hidden>
    Top Center
  </button>
  <button class="btn secondary" type="button" data-demo-position="top-end" hidden>
    Top End
  </button>
  <button class="btn secondary" type="button" data-demo-position="bottom-start" hidden>
    Bottom Start
  </button>
  <button class="btn secondary" type="button" data-demo-position="bottom-center" hidden>
    Bottom Center
  </button>
  <button class="btn secondary" type="button" data-demo-position="bottom-end" hidden>
    Bottom End
  </button>
</div>
<div
  class="toasts"
  id="position-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
>
</div>
<script>
  addEventListener('DOMContentLoaded', () => {
  const region = document.getElementById('position-toasts');
  document.querySelectorAll('[data-demo-position]').forEach(button => {
    button.hidden = false;
    button.addEventListener('click', () => {
      region.toast.clear();
      region.dataset.position = button.dataset.demoPosition;
      region.toast.show({ title: 'Changes saved', description: 'Your preferences are up to date.', tone: 'success' });
    });
  });
});
</script>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Right-to-left

Hebrew content and logical spacing mirror the placement and controls.

View Code
HTML
<div dir="rtl" lang="he">
  <button
    class="btn secondary"
    type="button"
    data-toast-target="rtl-toasts"
    data-toast-title="השינויים נשמרו"
    data-toast-description="ההעדפות שלך מעודכנות."
    hidden
  >
    הצגת הודעה
  </button>
  <div
    class="toasts"
    id="rtl-toasts"
    data-toasts
    role="region"
    aria-label="התראות"
    data-dismiss="סגירה"
  >
  </div>
</div>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Without motion

Use data-motion="none" on the region to remove entry motion. The system reduced-motion preference is also respected.

View Code
HTML
<button
  class="btn secondary"
  type="button"
  data-toast-target="still-toasts"
  data-toast-title="Preferences updated"
  data-toast-description="Your changes are ready."
  hidden
>
  Show Without Motion
</button>
<div
  class="toasts"
  id="still-toasts"
  data-toasts
  role="region"
  aria-label="Notifications"
  data-motion="none"
>
</div>
<noscript>
  <p>
    Enable JavaScript to try notifications. Keep important results in the page as well.
  </p>
</noscript>

Installation

Download these files into the matching paths in your project, or use Create to assemble a kit. Include each dependency once. Do not also load app.css.

Add to your page
<link rel="stylesheet" href="/library/assets/base.css">
<link rel="stylesheet" href="/library/assets/button.css">
<link rel="stylesheet" href="/library/assets/spinner.css">
<link rel="stylesheet" href="/library/assets/toast.css">
<script defer src="/library/assets/toast.js"></script>

13,871 bytes CSS including base · 6,467 bytes runtime JavaScript

Usage

Choose a placement

Set data-position on the region: top-start, top-center, top-end, bottom-start, bottom-center, or bottom-end. The default is bottom-center. Start and end mirror in RTL. Choose one consistent position for each notification stream.

Use meaningful status icons

Success uses a check circle, information an info circle, warning a triangle, and error an x circle. Shared SVG artwork comes from the icon catalog. Keep visible text alongside every icon; color alone must not convey the result.

Confirm a result

Show a toast after an operation finishes. Keep important errors, receipts, and recovery steps visible in the page too. Toasts do not save or send anything.

Give people enough time

Messages stay until dismissed by default. Timed messages last at least six seconds and pause on hover, keyboard focus, or a background tab. Errors and messages with actions stay visible.

Keep announcements calm

Routine feedback is announced politely. Use urgent: true only for time-sensitive information that warrants interrupting a screen reader. A toast never moves focus on arrival.

Handle several messages

Up to three messages are visible at once; additional messages wait their turn, up to twenty total. show() returns null when full. Update an existing message for progress instead of generating repeated notifications.

Use the keyboard

Tab reaches actions and Dismiss. Escape closes the focused toast and returns focus to its origin when available. Reduced motion removes entry animation. Translate the region name, content, action labels, and data-dismiss.

Building with AI? The AI Manifest has these notes and the full contract. See the AI Guide.