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
<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
<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
<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
<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
<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
<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
<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
<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
<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.
<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.