Toast
Generates toast notifications.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function ExampleToast() {
return (
<Toast.Provider>
<ToastButton />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function ToastButton() {
const toastManager = Toast.useToastManager();
const countRef = { current: 0 };
function createToast() {
countRef.current += 1;
toastManager.add({
title: `Toast ${countRef.current} created`,
description: 'This is a toast notification.',
});
}
return (
<button type="button" class={styles.Button} onClick={createToast}>
Create toast
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
Anatomy
Import the component and assemble its parts:
import { Toast } from 'base-ui-solid/toast';
<Toast.Provider>
<Toast.Portal>
<Toast.Viewport>
{/* Stacked toasts */}
<Toast.Root>
<Toast.Content>
<Toast.Title />
<Toast.Description />
<Toast.Action />
<Toast.Close />
</Toast.Content>
</Toast.Root>
{/* Anchored toasts */}
<Toast.Positioner>
<Toast.Root>
<Toast.Arrow />
<Toast.Content>
<Toast.Title />
<Toast.Description />
<Toast.Action />
<Toast.Close />
</Toast.Content>
</Toast.Root>
</Toast.Positioner>
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>;
General usage
<Toast.Provider>can be wrapped around your entire app, ensuring all toasts are rendered in the same viewport.- F6 lets users jump into the toast viewport landmark region to navigate toasts with keyboard focus.
- The
data-base-ui-swipe-ignoreattribute can be manually added to elements inside of a toast to prevent swipe-to-dismiss gestures on them. Interactive elements are automatically prevented.
Global manager
A global toast manager can be created by passing the toastManager prop to the <Toast.Provider>.
This enables you to queue a toast from anywhere in the app (such as in functions outside the Solid tree) while still using the same toast renderer.
The created toastManager exposes the same add, close, update, and promise methods as the Toast.useToastManager() hook. Unlike the hook, it does not return the reactive toasts array, since it lives outside the Solid tree.
const toastManager = Toast.createToastManager();
<Toast.Provider toastManager={toastManager}>
Stacking and animations
The --toast-index CSS variable can be used to determine the stacking order of the toasts.
The 0th index toast appears at the front.
.Toast {
z-index: calc(1000 - var(--toast-index));
transform: scale(calc(max(0, 1 - (var(--toast-index) * 0.1))));
}
The --toast-offset-y CSS variable can be used to determine the vertical offset of the toasts when positioned absolutely with a translation offset — this is usually used with the data-expanded attribute, present when the toast viewport is being hovered or has focus.
.Toast[data-expanded] {
transform: translateY(var(--toast-offset-y));
}
While the stack is collapsed, each toast’s height can be clamped to the frontmost toast’s height using the --toast-frontmost-height CSS variable, and <Toast.Content> is used to hide the content of the toasts behind it.
The data-behind attribute marks content that sits behind the frontmost toast and pairs with the data-expanded attribute so the content fades back in when the viewport expands:
.Toast {
height: var(--toast-frontmost-height, var(--toast-height));
}
.ToastContent {
transition: opacity 0.25s;
}
.ToastContent[data-behind] {
opacity: 0;
}
.ToastContent[data-expanded] {
opacity: 1;
}
The --toast-swipe-movement-x and --toast-swipe-movement-y CSS variables are used to determine the swipe movement of the toasts in order to add a translation offset.
.Toast {
transform: scale(calc(max(0, 1 - (var(--toast-index) * 0.1))))
translateX(var(--toast-swipe-movement-x))
translateY(calc(var(--toast-swipe-movement-y) + (var(--toast-index) * -20%)));
}
The data-swipe-direction attribute can be used to determine the swipe direction of the toasts to add a translation offset upon dismissal.
&[data-ending-style] {
opacity: 0;
&[data-swipe-direction='up'] {
transform: translateY(calc(var(--toast-swipe-movement-y) - 150%));
}
&[data-swipe-direction='down'] {
transform: translateY(calc(var(--toast-swipe-movement-y) + 150%));
}
/* Note: --offset-y is defined locally in these examples and derives from
--toast-offset-y, --toast-index, and swipe movement values */
&[data-swipe-direction='left'] {
transform: translateX(calc(var(--toast-swipe-movement-x) - 150%)) translateY(var(--offset-y));
}
&[data-swipe-direction='right'] {
transform: translateX(calc(var(--toast-swipe-movement-x) + 150%)) translateY(var(--offset-y));
}
}
The data-limited attribute indicates that the toast exceeded the limit option.
Limited toasts remain mounted with the HTML inert attribute, so this is useful for hiding them or animating them differently.
The updateKey property increments when a toast is updated or upserted.
This can be used to replay attention-grabbing styles by switching animation names or, when remounting is acceptable, by including it in a Solid key.
Examples
Anchored toasts
Toasts can be anchored to a specific element using <Toast.Positioner> and the positionerProps option when adding a toast. This is useful for showing contextual feedback like transient “Copied” toasts that appear near the button that triggered the action.
Anchored toasts should be rendered in a separate <Toast.Provider> from stacked toasts. A global toast manager can be created for each to manage them separately throughout your app:
import { For } from 'solid-js';
const anchoredToastManager = Toast.createToastManager();
const stackedToastManager = Toast.createToastManager();
function App() {
return [
<Toast.Provider toastManager={anchoredToastManager}>
<AnchoredToasts />
</Toast.Provider>,
<Toast.Provider toastManager={stackedToastManager}>
<StackedToasts />
</Toast.Provider>,
];
}
function AnchoredToasts() {
const toastManager = Toast.useToastManager();
return (
<Toast.Portal>
<Toast.Viewport>
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Positioner toast={toast()}>
<Toast.Root toast={toast()}>{/* ... */}</Toast.Root>
</Toast.Positioner>
)}
</For>
</Toast.Viewport>
</Toast.Portal>
);
}
function StackedToasts() {
const toastManager = Toast.useToastManager();
return (
<Toast.Portal>
<Toast.Viewport>
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => <Toast.Root toast={toast()}>{/* ... */}</Toast.Root>}
</For>
</Toast.Viewport>
</Toast.Portal>
);
}
import { For, createSignal } from 'solid-js';
import type { JSX } from '@solidjs/web';
import { Toast } from 'base-ui-solid/toast';
import { Button } from 'base-ui-solid/button';
import { Tooltip } from 'base-ui-solid/tooltip';
import styles from './index.module.css';
const anchoredToastManager = Toast.createToastManager();
const stackedToastManager = Toast.createToastManager();
export default function ExampleToast() {
return (
<Tooltip.Provider>
<Toast.Provider toastManager={anchoredToastManager}>
<AnchoredToasts />
</Toast.Provider>
<Toast.Provider toastManager={stackedToastManager}>
<StackedToasts />
</Toast.Provider>
<div class={styles.ButtonGroup}>
<CopyButton />
<StackedToastButton />
</div>
</Tooltip.Provider>
);
}
function StackedToastButton() {
function createToast() {
stackedToastManager.add({
description: 'Copied',
});
}
return (
<button type="button" class={styles.Button} onClick={createToast}>
Stacked toast
</button>
);
}
function CopyButton() {
const [copied, setCopied] = createSignal(false);
let buttonRef: HTMLButtonElement | undefined;
function handleCopy() {
setCopied(true);
anchoredToastManager.add({
description: 'Copied',
positionerProps: {
anchor: buttonRef,
sideOffset: 10,
},
timeout: 1500,
onClose() {
setCopied(false);
},
});
}
return (
<Tooltip.Root disabled={copied()}>
<Tooltip.Trigger
ref={(element) => {
buttonRef = element;
}}
closeOnClick={false}
class={styles.CopyButton}
onClick={handleCopy}
aria-label="Copy to clipboard"
render={(props) => (
<Button
{...props}
style={props.style || undefined}
disabled={copied()}
focusableWhenDisabled
/>
)}
>
{copied() ? <CheckIcon /> : <ClipboardIcon />}
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={10}>
<Tooltip.Popup class={styles.Tooltip}>
<Tooltip.Arrow class={styles.Arrow} />
Copy
</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</Tooltip.Root>
);
}
function AnchoredToasts() {
const toastManager = Toast.useToastManager();
return (
<Toast.Portal>
<Toast.Viewport class={styles.AnchoredViewport}>
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Positioner toast={toast()} class={styles.AnchoredPositioner}>
<Toast.Root toast={toast()} class={styles.AnchoredToast}>
<Toast.Arrow class={styles.Arrow} />
<Toast.Content>
<Toast.Description class={styles.AnchoredDescription} />
</Toast.Content>
</Toast.Root>
</Toast.Positioner>
)}
</For>
</Toast.Viewport>
</Toast.Portal>
);
}
function StackedToasts() {
const toastManager = Toast.useToastManager();
return (
<Toast.Portal>
<Toast.Viewport class={styles.StackedViewport}>
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.StackedToast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
</Toast.Viewport>
</Toast.Portal>
);
}
function ClipboardIcon(
props: Omit<JSX.IntrinsicElements['svg'], 'style'> & {
style?: JSX.CSSProperties;
},
) {
return (
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
{...props}
style={{ display: 'block', ...props.style }}
>
<rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
</svg>
);
}
function CheckIcon(
props: Omit<JSX.IntrinsicElements['svg'], 'style'> & {
style?: JSX.CSSProperties;
},
) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
{...props}
style={{ display: 'block', ...props.style }}
>
<path d="m2.5 8.5 4 4 7-9" />
</svg>
);
}
Custom position
The position of the toasts is controlled by your own CSS.
To change the toasts’ position, you can modify the .Viewport and .Root styles.
A more general component could accept a data-position attribute, which the CSS handles for each variation.
The following shows a top-center position:
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function ExampleToast() {
return (
<Toast.Provider>
<ToastButton />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function ToastButton() {
const toastManager = Toast.useToastManager();
const countRef = { current: 0 };
function createToast() {
countRef.current += 1;
toastManager.add({
title: `Toast ${countRef.current} created`,
description: 'This is a toast notification.',
});
}
return (
<button type="button" class={styles.Button} onClick={createToast}>
Create toast
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} swipeDirection="up" class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
Undo action
When adding a toast, the actionProps option can be used to define props for an action button inside of it—this enables the ability to undo an action associated with the toast.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function UndoToastExample() {
return (
<Toast.Provider>
<Form />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function Form() {
const toastManager = Toast.useToastManager();
function action() {
const id = toastManager.add({
title: 'Action performed',
description: 'You can undo this action.',
type: 'success',
timeout: 10000,
actionProps: {
children: 'Undo',
onClick() {
toastManager.close(id);
toastManager.add({
title: 'Action undone',
});
},
},
});
}
return (
<button type="button" onClick={action} class={styles.Button}>
Perform action
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<div class={styles.Message}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Action class={styles.UndoButton} />
</div>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
Promise
An asynchronous toast can be created with three possible states: loading, success, and error.
The type string matches these states to change the styling.
Each of the states also accepts the method options object for more granular control.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function PromiseToastExample() {
return (
<Toast.Provider>
<PromiseDemo />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function PromiseDemo() {
const toastManager = Toast.useToastManager();
function runPromise() {
toastManager.promise(
// Simulate an API request with a promise that resolves after 2 seconds
new Promise<string>((resolve, reject) => {
const shouldSucceed = Math.random() > 0.3; // 70% success rate
setTimeout(() => {
if (shouldSucceed) {
resolve('operation completed');
} else {
reject(new Error('operation failed'));
}
}, 2000);
}),
{
loading: 'Loading data…',
success: (data: string) => `Success: ${data}`,
error: (err: Error) => `Error: ${err.message}`,
},
);
}
return (
<button type="button" onClick={runPromise} class={styles.Button}>
Run promise
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
Custom
A toast with custom data can be created by passing any typed object interface to the data option.
This enables you to pass any data (including functions) you need to the toast and access it in the toast’s rendering logic.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
interface CustomToastData {
userId: string;
}
function isCustomToast(
toast: Toast.Root.ToastObject,
): toast is Toast.Root.ToastObject<CustomToastData> {
return toast.data?.userId !== undefined;
}
export default function CustomToastExample() {
return (
<Toast.Provider>
<CustomToast />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function CustomToast() {
const toastManager = Toast.useToastManager();
function action() {
const data: CustomToastData = {
userId: '123',
};
toastManager.add({
title: 'Toast with custom data',
data,
});
}
return (
<button type="button" onClick={action} class={styles.Button}>
Create custom toast
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title}>{toast().title}</Toast.Title>
{isCustomToast(toast()) && toast().data ? (
<Toast.Description class={styles.Description}>
data.userId is {toast().data.userId}
</Toast.Description>
) : (
<Toast.Description class={styles.Description} />
)}
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
Deduplicated toast
When you upsert the same toast by id, the updateKey property increments so a custom renderer can replay a visual animation. This demo alternates CSS animation names from updateKey, which keeps the same toast mounted while replaying the pulse.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function PulseToast() {
return (
<Toast.Provider>
<PulseToastButton />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function PulseToastButton() {
const toastManager = Toast.useToastManager();
function createToast() {
toastManager.add({
id: 'save-status',
title: 'Draft saved',
description: 'Click again while it is visible to replay the pulse.',
});
}
return (
<button type="button" onClick={createToast} class={styles.Button}>
Save draft
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => <PulseToastItem toast={toast()} />}
</For>
);
}
function PulseToastItem(props: { toast: Toast.Root.ToastObject }) {
const pulseClass = () => {
if (!props.toast.updateKey) {
return undefined;
}
return props.toast.updateKey % 2 === 0 ? styles.PulseEven : styles.PulseOdd;
};
return (
<Toast.Root toast={props.toast} class={[styles.Toast, pulseClass()]}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
);
}
Varying heights
Toasts with varying heights are stacked by clamping every toast’s height to the frontmost toast at index 0 using the --toast-frontmost-height CSS variable, while the data-behind attribute hides the content of the toasts behind it.
Avoid sizing <Toast.Content> to the root’s height (such as height: 100%), as resizing it alongside the root cancels the root’s height transition.
import { For } from 'solid-js';
import { Toast } from 'base-ui-solid/toast';
import styles from './index.module.css';
export default function VaryingHeightsToast() {
return (
<Toast.Provider>
<ToastButton />
<Toast.Portal>
<Toast.Viewport class={styles.Viewport}>
<ToastList />
</Toast.Viewport>
</Toast.Portal>
</Toast.Provider>
);
}
function ToastButton() {
const toastManager = Toast.useToastManager();
const countRef = { current: 0 };
function createToast() {
countRef.current += 1;
const description = TEXTS[Math.floor(Math.random() * TEXTS.length)];
toastManager.add({
title: `Toast ${countRef.current} created`,
description,
});
}
return (
<button type="button" class={styles.Button} onClick={createToast}>
Create varying height toast
</button>
);
}
function ToastList() {
const toastManager = Toast.useToastManager();
return (
<For each={toastManager.toasts} keyed={(toast) => toast.id}>
{(toast) => (
<Toast.Root toast={toast()} class={styles.Toast}>
<Toast.Content class={styles.Content}>
<div class={styles.Text}>
<Toast.Title class={styles.Title} />
<Toast.Description class={styles.Description} />
</div>
<Toast.Close class={styles.Close}>Dismiss</Toast.Close>
</Toast.Content>
</Toast.Root>
)}
</For>
);
}
const TEXTS = [
'Short message.',
'A bit longer message that spans two lines.',
'This is a longer description that intentionally takes more vertical space to demonstrate stacking with varying heights.',
'An even longer description that should span multiple lines so we can verify the clamped collapsed height and smooth expansion animation when hovering or focusing the viewport.',
];
API reference
Provider
Provides a context for creating and managing toasts.
limitnumber3
limited (via the data-limited
attribute) rather than removed, so they can be hidden or animated out.numbertoastManagerToastManager—
ToastManagertimeoutnumber5000
0 will prevent the toast from being dismissed automatically.numberchildrenJSX.Element—
JSX.ElementProvider.State
type ToastProviderState = {};Portal
A portal element that moves the viewport to a different part of the DOM.
By default, the portal element is appended to <body>.
Renders a <div> element.
containerUnion—
HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | nullclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Portal.State
type ToastPortalState = {};Viewport
A container viewport for toasts.
Renders a <div> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-expandedboolean—
booleanAttribute | Description | |
|---|---|---|
data-expanded | Indicates toasts are expanded in the viewport. | |
CSS variables
--toast-frontmost-heightnumber—
numberCSS Variable | Description | |
|---|---|---|
--toast-frontmost-height | Indicates the height of the frontmost toast. | |
Viewport.State
type ToastViewportState = {
/** Whether toasts are expanded in the viewport. */
expanded: boolean;
};Root
Groups all parts of an individual toast.
Renders a <div> element.
swipeDirectionUnion['down', 'right']
'up' | 'down' | 'left' | 'right' | ('left' | 'right' | 'up' | 'down')[]toast*Toast.Root.ToastObject—
Toast.Root.ToastObjectclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-expandedboolean—
booleandata-limitedboolean—
booleandata-swipe-directionUnion—
'up' | 'down' | 'left' | 'right'data-swipingboolean—
booleandata-typestring—
stringdata-starting-style-—
-data-ending-style-—
-Attribute | Description | |
|---|---|---|
data-expanded | Present when the toast is expanded in the viewport. | |
data-limited | Present when the toast was limited because the toast limit was exceeded. | |
data-swipe-direction | The direction the toast was swiped. | |
data-swiping | Present when the toast is being swiped. | |
data-type | The type of the toast. | |
data-starting-style | Present when the toast begins animating in. | |
data-ending-style | Present when the toast is animating out. | |
CSS variables
--toast-heightnumber—
number--toast-indexnumber—
number--toast-offset-ynumber—
number--toast-swipe-movement-xnumber—
number--toast-swipe-movement-ynumber—
numberCSS Variable | Description | |
|---|---|---|
--toast-height | Indicates the measured natural height of the toast in pixels. | |
--toast-index | Indicates the index of the toast in the list. | |
--toast-offset-y | Indicates the vertical pixels offset of the toast in the list when expanded. | |
--toast-swipe-movement-x | Indicates the horizontal swipe movement of the toast. | |
--toast-swipe-movement-y | Indicates the vertical swipe movement of the toast. | |
Root.State
type ToastRootState = {
/** The transition status of the component. */
transitionStatus: TransitionStatus;
/** Whether the toasts in the viewport are expanded. */
expanded: boolean;
/** Whether the toast was limited because the toast limit was exceeded. */
limited: boolean;
/** The type of the toast. */
type: string | undefined;
/** Whether the toast is being swiped. */
swiping: boolean;
/** The direction the toast is being swiped. */
swipeDirection: 'up' | 'down' | 'left' | 'right' | undefined;
};Root.ToastObject
type ToastRootToastObject<Data extends {} = any> = {
/** The unique identifier for the toast. */
id: string;
/** The ref for the toast. */
ref?: RefObject<HTMLElement | null>;
/** The title of the toast. */
title?: JSX.Element;
/**
* The type of the toast. Used to conditionally style the toast,
* including conditionally rendering elements based on the type.
*/
type?: string;
/** The description of the toast. */
description?: JSX.Element;
/**
* The amount of time (in ms) before the toast is auto dismissed.
* A value of `0` will prevent the toast from being dismissed automatically.
* @default 5000
*/
timeout?: number;
/**
* The priority of the toast.
* - `low` - The toast will be announced politely.
* - `high` - The toast will be announced urgently.
* @default 'low'
*/
priority?: 'low' | 'high';
/** The transition status of the toast. */
transitionStatus?: 'starting' | 'ending';
/** A counter that increments whenever the toast is updated or upserted. */
updateKey?: number;
/** Determines if the toast was limited because the toast limit was exceeded. */
limited?: boolean;
/** The height of the toast. */
height?: number;
/** Callback function to be called when the toast is closed. */
onClose?: () => void;
/** Callback function to be called when the toast is removed from the list after any animations are complete when closed. */
onRemove?: () => void;
/** The props for the action button. */
actionProps?: Omit<
JSX.IntrinsicElements['button'],
'ref'
>;
/** The props forwarded to the toast positioner element when rendering anchored toasts. */
positionerProps?: ToastManagerPositionerProps;
/** Custom data for the toast. */
data?: Data;
};Content
A container for the contents of a toast.
Renders a <div> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-behindboolean—
booleandata-expandedboolean—
booleanAttribute | Description | |
|---|---|---|
data-behind | Present when the toast is behind the frontmost toast in the stack. | |
data-expanded | Present when the toast viewport is expanded. | |
Content.State
type ToastContentState = {
/** Whether the toast viewport is expanded. */
expanded: boolean;
/** Whether the toast is behind the frontmost toast in the stack. */
behind: boolean;
};Title
A title that labels the toast.
Renders an <h2> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-typestring—
stringAttribute | Description | |
|---|---|---|
data-type | The type of the toast. | |
Title.State
type ToastTitleState = {
/** The type of the toast. */
type: string | undefined;
};Description
A description that describes the toast.
Can be used as the default message for the toast when no title is provided.
Renders a <p> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-typestring—
stringAttribute | Description | |
|---|---|---|
data-type | The type of the toast. | |
Description.State
type ToastDescriptionState = {
/** The type of the toast. */
type: string | undefined;
};Action
Performs an action when clicked.
Renders a <button> element.
nativeButtonbooleantrue
<button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).booleanclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-typestring—
stringAttribute | Description | |
|---|---|---|
data-type | The type of the toast. | |
Action.State
type ToastActionState = {
/** The type of the toast. */
type: string | undefined;
};Close
Closes the toast when clicked.
Renders a <button> element.
nativeButtonbooleantrue
<button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).booleanclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-typestring—
stringAttribute | Description | |
|---|---|---|
data-type | The type of the toast. | |
Close.State
type ToastCloseState = {
/** The type of the toast. */
type: string | undefined;
};Positioner
Positions the toast against the anchor.
Renders a <div> element.
disableAnchorTrackingbooleanfalse
booleantoast*ToastObject<any>—
ToastObject<any>alignAlign'center'
AlignalignOffsetUnion0
data object parameter with the following properties: data.anchor: the dimensions of the anchor element with properties width and height.data.positioner: the dimensions of the positioner element with properties width and height.data.side: which side of the anchor element the positioner is aligned against.data.align: how the positioner is aligned relative to the specified side.number | OffsetFunctionsideSide'top'
SidesideOffsetUnion0
data object parameter with the following properties: data.anchor: the dimensions of the anchor element with properties width and height.data.positioner: the dimensions of the positioner element with properties width and height.data.side: which side of the anchor element the positioner is aligned against.data.align: how the positioner is aligned relative to the specified side.number | OffsetFunctionarrowPaddingnumber5
numberanchorUnion—
Element | nullcollisionAvoidanceCollisionAvoidance—
side controls overflow on the preferred placement axis (top/bottom or left/right): 'flip': keep the requested side when it fits; otherwise try the opposite side
(top and bottom, or left and right).'shift': never change side; keep the requested side and move the popup within
the clipping boundary so it stays visible.'none': do not correct side-axis overflow. align controls overflow on the alignment axis (start/center/end): 'flip': keep side, but swap start and end when the requested alignment overflows.'shift': keep side and requested alignment, then nudge the popup along the
alignment axis to fit.'none': do not correct alignment-axis overflow. fallbackAxisSide controls fallback behavior on the perpendicular axis when the
preferred axis cannot fit: 'start': allow perpendicular fallback and try the logical start side first
(top before bottom, or left before right in LTR).'end': allow perpendicular fallback and try the logical end side first
(bottom before top, or right before left in LTR).'none': do not fallback to the perpendicular axis. When side is 'shift', explicitly setting align only supports 'shift' or 'none'.
If align is omitted, it defaults to 'flip'.CollisionAvoidancecollisionBoundaryBoundary'clipping-ancestors'
BoundarycollisionPaddingPadding5
Paddingstickybooleanfalse
booleanpositionMethodUnion'absolute'
position property to use.'absolute' | 'fixed'classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-anchor-hidden-—
-data-alignUnion—
'start' | 'center' | 'end'data-sideUnion—
'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'Attribute | Description | |
|---|---|---|
data-anchor-hidden | Present when the anchor is hidden. | |
data-align | Indicates how the toast is aligned relative to specified side. | |
data-side | Indicates which side the toast is positioned relative to the trigger. | |
CSS variables
--anchor-heightnumber—
number--anchor-widthnumber—
number--available-heightnumber—
number--available-widthnumber—
number--transform-originstring—
stringCSS Variable | Description | |
|---|---|---|
--anchor-height | The anchor’s height. | |
--anchor-width | The anchor’s width. | |
--available-height | The available height between the anchor and the edge of the viewport. | |
--available-width | The available width between the anchor and the edge of the viewport. | |
--transform-origin | The coordinates that this element is anchored to. Used for animations and transitions. | |
Positioner.State
type ToastPositionerState = {
/** The side of the anchor the component is placed on. */
side: Side;
/** The alignment of the component relative to the anchor. */
align: Align;
/** Whether the anchor element is hidden. */
anchorHidden: boolean;
};Arrow
Displays an element positioned against the toast anchor.
Renders a <div> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-uncentered-—
-data-alignUnion—
'start' | 'center' | 'end'data-sideUnion—
'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'Attribute | Description | |
|---|---|---|
data-uncentered | Present when the toast arrow is uncentered. | |
data-align | Indicates how the toast is aligned relative to specified side. | |
data-side | Indicates which side the toast is positioned relative to the anchor. | |
Arrow.State
type ToastArrowState = {
/** The side of the anchor the component is placed on. */
side: Side;
/** The alignment of the component relative to the anchor. */
align: Align;
/** Whether the arrow cannot be centered on the anchor. */
uncentered: boolean;
};useToastManager
Manages toasts, called inside of a <Toast.Provider>.
const toastManager = Toast.useToastManager();
Returns the array of toasts and methods to manage them.
useToastManager
type ReturnValue = UseToastManagerReturnValue<{}>;add method
Creates a toast by adding it to the toast list.
If you pass an id that already exists, the existing toast is updated in place instead of creating a duplicate.
Returns a toastId that can be used to update or close the toast later.
const toastId = toastManager.add({
description: 'Hello, world!',
});
function App() {
const toastManager = Toast.useToastManager();
return (
<button
type="button"
onClick={() => {
toastManager.add({
description: 'Hello, world!',
});
}}
>
Add toast
</button>
);
}
For high priority toasts, the title and description strings are what are used to announce the toast to screen readers.
Screen readers do not announce any extra content rendered inside <Toast.Root>, including the <Toast.Title> or <Toast.Description> components, unless they intentionally navigate to the toast viewport.
update method
Updates the toast with new options.
toastManager.update(toastId, {
description: 'New description',
});
Options replace the corresponding values of the toast, including custom data, which is replaced as a whole. To derive the update from the current state of the toast, pass a function instead. It receives the current toast and returns the options to apply. Custom data is undefined when the toast has none yet:
toastManager.update(toastId, (prevToast) => ({
data: prevToast.data && { ...prevToast.data, progress: 100 },
}));
close method
Closes the toast, removing it from the toast list after any animations complete.
toastManager.close(toastId);
Or you can close all toasts at once by not passing an ID:
toastManager.close();
promise method
Creates an asynchronous toast with three possible states: loading, success, and error.
const promise = toastManager.promise(
new Promise((resolve) => {
setTimeout(() => resolve('world!'), 1000);
}),
{
// Each are a shortcut for the `description` option
loading: 'Loading…',
success: (data) => `Hello ${data}`,
error: (err) => `Error: ${err}`,
},
);
Each state also accepts the method options object to granularly control the toast for each state:
const promise = toastManager.promise(
new Promise((resolve) => {
setTimeout(() => resolve('world!'), 1000);
}),
{
loading: {
title: 'Loading…',
description: 'The promise is loading.',
},
success: {
title: 'Success',
description: 'The promise resolved successfully.',
},
error: {
title: 'Error',
description: 'The promise rejected.',
actionProps: {
children: 'Contact support',
onClick() {
// Redirect to support page
},
},
},
},
);
See full promise method
Additional types
ToastManagerUpdateOptions
type ToastManagerUpdateOptions<Data extends {}> = {
/** The title of the toast. */
title?: JSX.Element;
/**
* The type of the toast. Used to conditionally style the toast,
* including conditionally rendering elements based on the type.
*/
type?: string;
/** The description of the toast. */
description?: JSX.Element;
/**
* The amount of time (in ms) before the toast is auto dismissed.
* A value of `0` will prevent the toast from being dismissed automatically.
* @default 5000
*/
timeout?: number;
/**
* The priority of the toast.
* - `low` - The toast will be announced politely.
* - `high` - The toast will be announced urgently.
* @default 'low'
*/
priority?: 'low' | 'high';
/** Callback function to be called when the toast is closed. */
onClose?: () => void;
/** Callback function to be called when the toast is removed from the list after any animations are complete when closed. */
onRemove?: () => void;
/** The props for the action button. */
actionProps?: Omit<
JSX.IntrinsicElements['button'],
'ref'
>;
/** The props forwarded to the toast positioner element when rendering anchored toasts. */
positionerProps?: ToastManagerPositionerProps;
/** Custom data for the toast. */
data?: Data;
};