Toaster
ui-toaster shows toasts, which are brief notifications stacked in a corner of the viewport. Place one <ui-toaster> per
page, usually at the end of <body>. It listens for a toast event on document, so any script or component can
show a toast without a reference to the toaster:
document.dispatchEvent(new CustomEvent("toast", { detail: { title: "Saved" } }));The toast list is a popover="manual" element in the top layer with aria-live="polite", so screen readers announce
new toasts. Each toast closes after duration milliseconds or when its dismiss button is clicked.
This page already has a ui-toaster, so the demos below only send toast events or call its methods.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toaster demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>
<ui-toaster></ui-toaster>
<ui-button variant="outline" onclick="document.dispatchEvent(new CustomEvent('toast', { detail: { title: 'Event created', description: 'Sunday, December 3 at 9:00 AM' } }))">Show toast</ui-button>
</body>
</html>Examples
Destructive
variant: "destructive" colours the toast for errors.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toaster demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>
<ui-toaster></ui-toaster>
<ui-button variant="outline" onclick="document.dispatchEvent(new CustomEvent('toast', { detail: { title: 'Upload failed', description: 'The file is larger than 10 MB.', variant: 'destructive' } }))">Show error</ui-button>
</body>
</html>From a component
Inside a component, dispatch the same event from a script function.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toaster demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>
<ui-toaster></ui-toaster>
<import-element from="./toaster-save-demo.html"></import-element>
<toaster-save-demo></toaster-save-demo>
</body>
</html><custom-element name="toaster-save-demo">
<script>
const draft = useState({ saves: 0 });
function save() {
draft.saves++;
document.dispatchEvent(
new CustomEvent("toast", { detail: { title: "Draft saved", description: `Version ${draft.saves}` } }),
);
}
</script>
<ui-button onclick="save()">Save draft</ui-button>
</custom-element>Sticky toasts and dismissing from script
duration in the detail overrides the toaster's default for one toast. 0 keeps it open until it is dismissed. The
toast() method returns an id that you can pass to dismiss().
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toaster demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>
<ui-toaster></ui-toaster>
<import-element from="./toaster-sticky-demo.html"></import-element>
<toaster-sticky-demo></toaster-sticky-demo>
</body>
</html><custom-element name="toaster-sticky-demo">
<script>
const upload = useState({ id: 0 });
function start() {
if (upload.id) return;
upload.id = document.querySelector("ui-toaster").toast({ title: "Uploading...", description: "Keep this tab open.", duration: 0 });
}
function finish() {
if (!upload.id) return;
document.querySelector("ui-toaster").dismiss(upload.id);
upload.id = 0;
document.dispatchEvent(new CustomEvent("toast", { detail: { title: "Upload complete" } }));
}
</script>
<style>
:host {
display: flex;
gap: calc(var(--spacing) * 2);
}
</style>
<ui-button variant="outline" onclick="start()">Start upload</ui-button>
<ui-button variant="outline" onclick="finish()">Finish upload</ui-button>
</custom-element>Position and duration
Set position and duration on the page's toaster. Toasts stack from the chosen corner or edge.
<ui-toaster position="top-center" duration="6000"></ui-toaster>API
Parts
| Part | Slot | Renders |
|---|---|---|
ui-toaster |
The fixed toast list. Each toast is rendered from the data passed to toast() or the toast event, so there are no toast parts to write. |
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
position |
"bottom-right" | "bottom-left" | "bottom-center" | "top-right" | "top-left" | "top-center" |
"bottom-right" |
Where toasts appear. |
duration |
number | 4000 |
Default milliseconds before a toast closes. 0 keeps toasts open until dismissed. Minimum 0. |
Toast options
The detail of a toast event and the argument of toast() take the same fields. All are optional.
| Field | Type | Default | Description |
|---|---|---|---|
title |
string | "" |
Bold first line. |
description |
string | "" |
Muted second line. Hidden when empty. |
variant |
"default" | "destructive" |
"default" |
Destructive toasts use the destructive colour. |
duration |
number | the duration attribute |
Milliseconds before this toast closes. 0 keeps it open. |
Properties and methods
| Name | Description |
|---|---|
toast(options?) |
Shows a toast and returns its numeric id. |
dismiss(id) |
Removes the toast with that id. Unknown ids are ignored. |
Events
| Event | Target | Detail | Description |
|---|---|---|---|
toast |
document |
toast options | Listened for, not dispatched. Every connected ui-toaster shows a toast for each event. |
CSS parts
| Part | Description |
|---|---|
region |
The <ol> that holds the toasts (popover="manual", aria-live="polite"). |
toast |
Each toast <li>. Destructive toasts have data-variant="destructive". |
Accessibility
- The list is a polite live region, so new toasts are announced without moving focus.
- Each toast has a dismiss button with
aria-label="Dismiss". - Toasts close on their own. Do not put the only copy of important information or actions in a toast.