Skip to main content

Toast provides brief feedback after an action.

Loading...

Features

  • Supports screen readers
  • Limits the number of visible toasts
  • Handles promise lifecycles
  • Pauses on hover, focus, or page idle
  • Supports programmatic update/remove

Installation

Install the toast package:

npm install @zag-js/toast @zag-js/vue # or yarn add @zag-js/toast @zag-js/vue

Anatomy

Check the toast anatomy and part names.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

Import the toast package:

import * as toast from "@zag-js/toast"

Then use the framework integration helpers:

<script setup> import * as toast from "@zag-js/toast" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" // 1. Create the toaster const toaster = toast.createStore({ placement: "top-end", overlap: true, }) </script> <script setup> // 2. Design the toast component const props = defineProps<{ toast: toast.Options, index: number, parent: toast.GroupService }>() const machineProps = computed(() => ({ ...props.toast, parent: props.parent, index: props.index })) const service = useMachine(toast.machine, machineProps) const api = computed(() => toast.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <h3 v-bind="api.getTitleProps()">{{ api.title }}</h3> <p v-bind="api.getDescriptionProps()">{{ api.description }}</p> <button @click="api.dismiss()">Close</button> </div> </template> <script setup> // 3. Design the toaster const service = useMachine(toast.group.machine, { id: "1", store: toaster }) const api = toast.group.connect(service, normalizeProps) </script> <template> <div v-bind="api.getGroupProps()"> <Toast v-for="toast in api.getToasts()" :key="toast.id" :toast="toast" :index="index" :parent="service" /> </div> <RestOfYourApp /> </template> <script setup> // 4. Within your app const topRightToast = () => toast.create({ title: "Hello" }) const bottomRightToast = () => toast.create({ title: "Data submitted!", type: "success" }) </script> <template> <button @click="topRightToast">Add top-right toast</button> <button @click="bottomRightToast">Add bottom-right toast</button> </template>

To use toast effectively, understand these key parts:

Toast Group

  • toast.group.machine - State machine logic for the toast region.

  • toast.group.connect - Maps group state to JSX props and subscriptions.

    We recommend setting up the toast group machine once at the root of your project.

Toast Item

  • toast.machine - State machine logic for a single toast.
  • toast.connect - Maps toast state to JSX props and controls.

Creating a toast

Common toast types are info, success, warning, loading, and error. You can also pass a custom type string.

Helper methods are also available: toaster.info(...), toaster.success(...), toaster.warning(...), toaster.error(...), and toaster.loading(...).

To create a toast, use the toaster.create(...) method.

toaster.create({ title: "Hello World", description: "This is a toast", type: "info", })

The options you can pass in are:

  • title — The title of the toast.
  • description — The description of the toast.
  • type — The type of the toast. Can be either error, success , info, warning, loading, or any custom string.
  • duration — The duration of the toast. The default duration is computed based on the specified type.
  • onStatusChange — A callback that listens for the status changes across the toast lifecycle.
  • removeDelay — The delay before unmounting the toast from the DOM. Useful for transition.
  • action — Optional action button with label and onClick.
  • closable — Whether to render a close trigger.

Changing the placement

Placement is configured on the toast store and applies to the whole toast group.

const toaster = toast.createStore({ placement: "top-start", })

Overlapping toasts

When multiple toasts are created, they are rendered in a stack. To make the toasts overlap, set the overlap property to true.

const toaster = toast.createStore({ overlap: true, })

Be sure to set up the required styles to make overlap work correctly.

Changing the duration

Every toast has a default visible duration depending on the type set. Here's the following toast types and matching default durations:

typeduration
info5000
error5000
success2000
loadingInfinity

You can override the duration of the toast by passing the duration property to the toaster.create(...) function.

toaster.create({ title: "Hello World", description: "This is a toast", type: "info", duration: 6000, })

Use toaster.create(...) for new toasts and toaster.update(id, ...) to modify an existing one.

Using portals

Using a portal is helpful to ensure that the toast is rendered outside the DOM hierarchy of the parent component. To render the toast in a portal, wrap the rendered toasts in the ToastProvider within your framework-specific portal.

Snippet not found :(

Programmatic control

To update a toast programmatically, you need access to the unique identifier of the toast.

This identifier can be either:

  • the id passed into toaster.create(...) or,
  • the returned random id when the toaster.create(...) is called.

You can use any of the following methods to control a toast:

  • toaster.update(...) — Updates a toast.
  • toaster.remove(...) — Removes a toast instantly without delay.
  • toaster.dismiss(...) — Removes a toast with delay.
  • toaster.pause(...) — Pauses a toast.
  • toaster.resume(...) — Resumes a toast.
  • toaster.isVisible(...) — Checks if a toast is currently visible.
  • toaster.isDismissed(...) — Checks if a toast has been dismissed.
// grab the id from the created toast const id = toaster.create({ title: "Hello World", description: "This is a toast", type: "info", duration: 6000, }) // update the toast toaster.update(id, { title: "Hello World", description: "This is a toast", type: "success", }) // remove the toast toaster.remove(id) // dismiss the toast toaster.dismiss(id)

Pause and resume all toasts

Call pause()/resume() without an id to affect all active toasts.

toaster.pause() toaster.resume()

Handling promises

The toast group API exposes a toaster.promise() function to allow you update the toast when it resolves or rejects.

With the promise API, you can pass the toast options for each promise lifecycle. The loading option is required

toaster.promise(promise, { loading: { title: "Loading", description: "Please wait...", }, success: (data) => ({ title: "Success", description: "Your request has been completed", }), error: (err) => ({ title: "Error", description: "An error has occurred", }), })

toaster.promise(...) returns { id, unwrap }, so you can await the original result:

const result = toaster.promise(fetchData(), { loading: { title: "Loading..." }, }) await result?.unwrap()

Pausing the toasts

There are three scenarios that pause toast timeout:

  • When a user hovers or focuses the toast region.
  • When the document loses focus or the page is idle (e.g. switching to a new browser tab), controlled via the pauseOnPageIdle store option.
  • When the toaster.pause(id) is called.
// Global pause options const toaster = toast.createStore({ pauseOnPageIdle: true, }) // Programmatically pause a toast (by `id`) // `id` is the return value of `toaster.create(...)` toaster.pause(id)

Limiting the number of toasts

Toasts are great but displaying too many of them can sometimes hamper the user experience. To limit visible toasts, set max on the store.

const toaster = toast.createStore({ max: 10, })

Focus Hotkey for toasts

When a toast is created, you can focus the toast region by pressing the alt + T. This is useful for screen readers and keyboard navigation.

Set the hotkey store option to change the underlying hotkey.

const service = useMachine(toast.group.machine, { store: toast.createStore({ hotkey: ["F6"] }), })

Listening for toast lifecycle

When a toast is created, you can listen for the status changes across its lifecycle using the onStatusChange callback when you call toaster.create(...).

The status values are:

  • visible - The toast is mounted and rendered
  • dismissing - The toast is closing but still mounted
  • unmounted - The toast has been completely unmounted and no longer exists
toaster.info({ title: "Hello World", description: "This is a toast", type: "info", onStatusChange: (details) => { // details => { status: "visible" | "dismissing" | "unmounted" } console.log("Toast status:", details) }, })

Changing the gap between toasts

When multiple toasts are rendered, a gap of 16px is applied between each toast. To change this value, set gap on the store.

const toaster = toast.createStore({ gap: 24, })

Changing the offset

The toast region has a default 1rem offset from the viewport. Use the offsets store option to change this offset.

const toaster = toast.createStore({ offsets: "24px", })

Styling guide

Requirement

The toast machine injects a bunch of css variables that are required for it to work. You need to connect these variables in your styles.

[data-part="root"] { translate: var(--x) var(--y); scale: var(--scale); z-index: var(--z-index); height: var(--height); opacity: var(--opacity); will-change: translate, opacity, scale; }

To make transitions smooth, include transition properties.

[data-part="root"] { transition: translate 400ms, scale 400ms, opacity 400ms; transition-timing-function: cubic-bezier(0.21, 1.02, 0.73, 1); } [data-part="root"][data-state="closed"] { transition: translate 400ms, scale 400ms, opacity 200ms; transition-timing-function: cubic-bezier(0.06, 0.71, 0.55, 1); }

Toast styling

When a toast is created and the api.getRootProps() from the toast.connect is used, the toast will have a data-type that matches the specified type at its creation.

You can use this property to style the toast.

[data-part="root"][data-type="info"] { /* Styles for the specific toast type */ } [data-part="root"][data-type="error"] { /* Styles for the error toast type */ } [data-part="root"][data-type="success"] { /* Styles for the success toast type */ } [data-part="root"][data-type="loading"] { /* Styles for the loading toast type */ }

Methods and Properties

Machine API

The toast's api exposes the following methods:

  • getCount() => numberThe total number of toasts
  • getToasts() => ToastProps<any>[]The toasts
  • subscribe(callback: (toasts: Options<O>[]) => void) => VoidFunctionSubscribe to the toast group

Data Attributes

Root
data-scope
toast
data-part
root
data-state
"open" | "closed"
data-type
The type of the item
data-placement
The placement of the toast
data-mounted
Present when mounted
data-paused
Present when paused
data-overlap
Present when overlapping
GhostBefore
data-scope
toast
data-part
ghost-before
GhostAfter
data-scope
toast
data-part
ghost-after

CSS Variables

Content
--layer-index
The index of the dismissable in the layer stack
--nested-layer-count
The number of nested toasts
Backdrop
--layer-index
The index of the dismissable in the layer stack
Group
--viewport-offset-left
The left offset from the viewport
--viewport-offset-right
The right offset from the viewport
--viewport-offset-top
The top offset from the viewport
--viewport-offset-bottom
The bottom offset from the viewport
--gap
The gap between elements
--first-height
The height of the first toast
Edit this page on GitHub