Skip to main content
0.75.0
View Zag.js on Github
Join the Discord server

Toast

The toast component is used to give feedback to users after an action has taken place.

Properties

Features

  • Support for screen readers.
  • Limit the number of visible toasts.
  • Manage promises within toast.
  • Pause on hover, focus or page idle.
  • Can remove or update toast programmatically.

Installation

To use the toast machine in your project, run the following command in your command line:

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

This command will install the framework agnostic toast logic and the reactive utilities for your framework of choice.

Anatomy

To set up the toast correctly, you'll need to understand its anatomy and how we name its parts.

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

Usage

First, import the toast package into your project

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

Next, import the required hooks and functions for your framework and use the toast machine in your project 🔥

import { useActor, useMachine, normalizeProps } from "@zag-js/react" import * as toast from "@zag-js/toast" import { createContext } from "react" // 1. Create the single toast function Toast(props) { const [state, send] = useActor(props.actor) const api = toast.connect(state, send, normalizeProps) return ( <div {...api.getRootProps()}> <h3 {...api.getTitleProps()}>{api.title}</h3> <p {...api.getDescriptionProps()}>{api.description}</p> <button onClick={api.dismiss}>Close</button> </div> ) } // 2. Create the toast context const ToastContext = createContext() const useToast = () => useContext(ToastContext) // 3. Create the toast group provider export function ToastProvider({ children }) { const [state, send] = useMachine(toast.group.machine({ id: "1" })) const api = toast.group.connect(state, send, normalizeProps) return ( <ToastContext.Provider value={api}> {api.getPlacements().map((placement) => ( <div key={placement} {...api.getGroupProps({ placement })}> {api.getToastsByPlacement(placement).map((toast) => ( <Toast key={toast.id} actor={toast} /> ))} </div> ))} {children} </ToastContext.Provider> ) } // 4. Wrap your app with the toast group provider export function App() { return ( <ToastProvider> <ExampleComponent /> </ToastProvider> ) } // 5. Within your app function ExampleComponent() { const toast = useToast() return ( <div> <button onClick={() => { toast.create({ title: "Hello", placement: "top-end" }) }} > Add top-right toast </button> <button onClick={() => { toast.create({ title: "Data submitted!", type: "success", placement: "bottom-end", }) }} > Add bottom-right toast </button> </div> ) }

The toast consists of three key aspects:

Toast Item

  • toast.machine — The state machine representation of a single toast.
  • toast.connect — The function that takes the toast machine and returns methods and JSX properties.

Toast Group

  • toast.group.machine — The state machine representation of a group of toasts. It is responsible for spawning, updating and removing toasts.

  • toast.group.connect — function gives you access to methods you can use to add, update, and remove a toast.

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

Creating a toast

There are five toast types that can be created with the toast machine. info, success, loading, custom and error.

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

toast.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, loading, or custom.
  • 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.
  • placement — The placement of the toast.
  • removeDelay — The delay before unmounting the toast from the DOM. Useful for transition.

Changing the placement

Use the placement property when you call the toast.create(...) to change the position of the toast.

toast.info({ title: "Hello World", description: "This is a toast", 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 [state, send] = useMachine( toast.group.machine({ overlap: true, }), )

Be sure to set up the required styles to make the toasts overlap 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 toast.create(...) function.

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

You can also use the toast.upsert(...) function which creates or updates a toast.

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.

import { Portal } from "@zag-js/react" // ... // 3. Create the toast group provider, wrap your app with it export function ToastProvider() { const [state, send] = useMachine(toast.group.machine({ id: "1" })) const api = toast.group.connect(state, send, normalizeProps) return ( <ToastContext.Provider value={api}> <Portal> {api.getPlacements().map((placement) => ( <div key={placement} {...api.getGroupProps({ placement })}> {api.getToastsByPlacement(placement).map((toast) => ( <Toast key={toast.id} actor={toast} /> ))} </div> ))} </Portal> </ToastContext.Provider> ) }

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 toast.create(...) or,
  • the returned random id when the toast.create(...) is called.

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

  • toast.upsert(...) — Creates or updates a toast.
  • toast.update(...) — Updates a toast.
  • toast.remove(...) — Removes a toast instantly without delay.
  • toast.dismiss(...) — Removes a toast with delay.
  • toast.pause(...) — Pauses a toast.
  • toast.resume(...) — Resumes a toast.
// grab the id from the created toast const id = toast.create({ title: "Hello World", description: "This is a toast", type: "info", duration: 6000, placement: "top-start", }) // update the toast toast.update(id, { title: "Hello World", description: "This is a toast", type: "success", }) // remove the toast toast.remove(id) // dismiss the toast toast.dismiss(id)

Handling promises

The toast group API exposes a toast.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.

toast.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", }), })

Pausing the toasts

There are three scenarios we provide to pause a toast from timing out:

  • When the document loses focus or the page is idle (e.g. switching to a new browser tab), controlled via the pauseOnPageIdle context property.
  • When the toast.pause(id) provided by the toast.group.connect(...) is called.
// Global pause options const [state, send] = useMachine( toast.group.machine({ pauseOnPageIdle: true, }), ) // Programmatically pause a toast (by `id`) // `id` is the return value of `api.create(...)` toast.pause(id)

Limiting the number of toasts

Toasts are great but displaying too many of them can sometimes hamper the user experience. To limit the number of visible toasts, pass the max property to the group machine's context.

const [state, send] = useMachine( toast.group.machine({ 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 context property to change the underlying hotkey.

const [state, send] = useMachine( toast.group.machine({ 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 toast.create(...).

The status values are:

  • visible - The toast is mounted and rendered
  • dismissed - The toast is visually invisible but still mounted
  • unmounted - The toast has been completely unmounted and no longer exists
toast.info({ title: "Hello World", description: "This is a toast", type: "info", onStatusChange: (details) => { // details => { status: "visible" | "dismissed" | "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 the gap context property.

const [state, send] = useMachine( toast.group.machine({ gap: 24, }), )

Changing the offset

The toast region has a default 16px offset from the viewport. Use the offset context property to change the offset.

const [state, send] = useMachine( toast.group.machine({ 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 it transition smoothly, you should includes 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
  • getPlacements() => Placement[]The placements of the active toasts
  • getToastsByPlacement(placement: Placement) => Service<O>[]The active toasts by placement
  • isVisible(id: string) => booleanReturns whether the toast id is visible
  • create(options: Options<O>) => stringFunction to create a toast.
  • upsert(options: Options<O>) => stringFunction to create or update a toast.
  • update(id: string, options: Options<O>) => voidFunction to update a toast's options by id.
  • success(options: Options<O>) => stringFunction to create a success toast.
  • error(options: Options<O>) => stringFunction to create an error toast.
  • loading(options: Options<O>) => stringFunction to create a loading toast.
  • resume(id?: string) => voidFunction to resume a toast by id.
  • pause(id?: string) => voidFunction to pause a toast by id.
  • dismiss(id?: string) => voidFunction to dismiss a toast by id. If no id is provided, all toasts will be dismissed.
  • dismissByPlacement(placement: Placement) => voidFunction to dismiss all toasts by placement.
  • remove(id?: string) => voidFunction to remove a toast by id. If no id is provided, all toasts will be removed.
  • promise<T>(promise: Promise<T> | (() => Promise<T>), options: Options<O>>) => stringFunction to create a toast from a promise. - When the promise resolves, the toast will be updated with the success options. - When the promise rejects, the toast will be updated with the error options.
  • subscribe(callback: (toasts: Options<O>[]) => void) => VoidFunctionFunction to subscribe 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

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2024
On this page