packages/react-aria-components/docs/Toast.mdx
{/* Copyright 2025 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}
import {Layout} from '@react-spectrum/docs'; export default Layout;
import docs from 'docs:react-aria-components'; import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; import {StarterKits} from '@react-spectrum/docs/src/StarterKits'; import Anatomy from '/packages/react-aria/docs/toast/toast-anatomy.svg'; import {Keyboard} from '@react-spectrum/text'; import {InlineAlert, Content, Heading} from '@adobe/react-spectrum';
<PageDescription>{docs.exports.UNSTABLE_Toast.description}</PageDescription>
<HeaderInfo packageData={packageData} componentNames={['ToastRegion', 'Toast']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/'} ]} />
<InlineAlert variant="notice" marginTop={60}> <Heading>Under construction</Heading> <Content>This component is in <strong>alpha</strong>. More documentation is coming soon!</Content> </InlineAlert>First, render a ToastRegion in the root of your app.
import {UNSTABLE_ToastRegion as ToastRegion, UNSTABLE_Toast as Toast, UNSTABLE_ToastQueue as ToastQueue, UNSTABLE_ToastContent as ToastContent, Button, Text} from 'react-aria-components';
import {X} from 'lucide-react';
// Define the type for your toast content.
interface MyToastContent {
title: string,
description?: string
}
// Create a global ToastQueue.
export const queue = new ToastQueue<MyToastContent>();
// Render a <ToastRegion> in the root of your app.
export function App() {
return (
<>
<ToastRegion queue={queue}>
{({toast}) => (
<Toast toast={toast}>
<ToastContent>
<Text slot="title">{toast.content.title}</Text>
<Text slot="description">{toast.content.description}</Text>
</ToastContent>
<Button slot="close"><X size={16} /></Button>
</Toast>
)}
</ToastRegion>
</>
);
}
Then, you can trigger a toast from anywhere using the exported queue.
<Button
onPress={() => queue.add({
title: 'Toast complete!',
description: 'Great success.'
})}>
Toast
</Button>
@import './Button.mdx' layer(button);
@import "@react-aria/example-theme";
.react-aria-ToastRegion {
position: fixed;
bottom: 16px;
right: 16px;
display: flex;
flex-direction: column-reverse;
gap: 8px;
border-radius: 8px;
outline: none;
&[data-focus-visible] {
outline: 2px solid slateblue;
outline-offset: 2px;
}
}
.react-aria-Toast {
display: flex;
align-items: center;
gap: 16px;
background: slateblue;
color: white;
padding: 12px 16px;
border-radius: 8px;
outline: none;
&[data-focus-visible] {
outline: 2px solid slateblue;
outline-offset: 2px;
}
.react-aria-ToastContent {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-width: 0px;
[slot=title] {
font-weight: bold;
}
}
.react-aria-Button[slot=close] {
flex: 0 0 auto;
background: none;
border: none;
appearance: none;
border-radius: 50%;
height: 32px;
width: 32px;
font-size: 16px;
border: 1px solid white;
color: white;
padding: 0;
outline: none;
&[data-focus-visible] {
box-shadow: 0 0 0 2px slateblue, 0 0 0 4px white;
}
&[data-pressed] {
background: rgba(255, 255, 255, 0.2);
}
}
}
There is no built in way to display toast notifications in HTML. <ToastRegion> and <Toast> help achieve accessible toasts that can be styled as needed.
A <ToastRegion> is an ARIA landmark region labeled "Notifications" by default. A <ToastRegion> accepts a function to render one or more visible toasts, in chronological order. When the limit is reached, additional toasts are queued until the user dismisses one. Each <Toast> is a non-modal ARIA alertdialog, containing the content of the notification and a close button. The toast's content gets announced by screen readers when the toast appears, so it is recommended to render the close button as a sibling of <ToastContent> instead of inside it, but this is not a requirement.
Landmark regions including the toast container can be navigated using the keyboard by pressing the <Keyboard>F6</Keyboard> key to move forward, and the <Keyboard>Shift</Keyboard> + <Keyboard>F6</Keyboard> key to move backward. This provides an easy way for keyboard users to jump to the toasts from anywhere in the app. When the last toast is closed, keyboard focus is restored.
import {ToastRegion, Toast, ToastContent, Text, Button} from 'react-aria-components';
<ToastRegion>
{({toast}) => (
<Toast toast={toast}>
<ToastContent>
<Text slot="title" />
<Text slot="description" />
</ToastContent>
<Button slot="close" />
</Toast>
)}
</ToastRegion>
Toasts support a timeout option to automatically hide them after a certain amount of time. For accessibility, toasts should have a minimum timeout of 5 seconds to give users enough time to read them. If a toast includes action buttons or other interactive elements it should not auto dismiss. In addition, timers will automatically pause when the user focuses or hovers over a toast.
Be sure only to automatically dismiss toasts when the information is not important, or may be found elsewhere. Some users may require additional time to read a toast message, and screen zoom users may miss toasts entirely.
<Button
/*- begin highlight -*/
onPress={() => queue.add({title: 'Toast is done!'}, {timeout: 5000})}
/*- end highlight -*/
>
Show toast
</Button>
Toasts may be programmatically dismissed if they become irrelevant before the user manually closes them. queue.add returns a key for the toast which may be passed to queue.close to dismiss the toast.
function Example() {
let [toastKey, setToastKey] = React.useState(null);
return (
<Button
onPress={() => {
if (!toastKey) {
///- begin highlight -///
setToastKey(queue.add({title: 'Unable to save'}, {onClose: () => setToastKey(null)}));
///- end highlight -///
} else {
///- begin highlight -///
queue.close(toastKey);
///- end highlight -///
}
}}>
{toastKey ? 'Hide' : 'Show'} Toast
</Button>
);
}
Toast entry and exit animations can be done using third party animation libraries like Motion, or using native CSS view transitions.
This example shows how to use the wrapUpdate option of ToastQueue to wrap state updates in a CSS view transition. The toast.key can be used to assign a viewTransitionName to each Toast.
import {flushSync} from 'react-dom';
const queue = new ToastQueue<MyToastContent>({
/*- begin highlight -*/
// Wrap state updates in a CSS view transition.
wrapUpdate(fn) {
if ('startViewTransition' in document) {
document.startViewTransition(() => {
flushSync(fn);
});
} else {
fn();
}
}
/*- end highlight -*/
});
<ToastRegion queue={queue}>
{({toast}) => (
<Toast
/*- begin highlight -*/
style={{viewTransitionName: toast.key}}
/*- end highlight -*/
toast={toast}>
<ToastContent>
<Text slot="title">{toast.content.title}</Text>
<Text slot="description">{toast.content.description}</Text>
</ToastContent>
<Button slot="close"><X size={16} /></Button>
</Toast>
)}
</ToastRegion>
<Button onPress={() => queue.add({title: 'Toasted!'})}>Toast</Button>
.react-aria-Toast {
view-transition-class: toast;
}
::view-transition-new(.toast):only-child {
animation: slide-in 400ms;
}
::view-transition-old(.toast):only-child {
animation: slide-out 400ms;
}
@keyframes slide-out {
to {
translate: 100% 0;
opacity: 0;
}
}
@keyframes slide-in {
from {
translate: 100% 0;
opacity: 0;
}
}
<ToastRegion> renders a group of toasts.
<Toast> renders an individual toast.
<ToastContent> renders the main content of a toast, including the title and description. It accepts all HTML attributes.
A ToastQueue manages the state for a <ToastRegion>. The state is stored outside React so that you can trigger toasts from anywhere in your application, not just inside components.