Skip to contents

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:

Anatomy
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-ignore attribute 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.

Creating a manager instance
const toastManager = Toast.createToastManager();
Using the instance
<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.

z-index stacking
.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.

Expanded offset
.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:

Collapsed content
.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.

Swipe 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.

Swipe direction
&[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:

Mixing stacked and anchored toasts
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.

Prop
Type
Default
limitnumber3
The maximum number of toasts that can be displayed at once. When the limit is exceeded, the oldest toasts are marked as limited (via the data-limited attribute) rather than removed, so they can be hidden or animated out.number
toastManagerToastManager—
A global manager for toasts to use outside of a Solid component.ToastManager
timeoutnumber5000
The default amount of time (in ms) before a toast is auto dismissed. A value of 0 will prevent the toast from being dismissed automatically.number
childrenJSX.Element—
-JSX.Element
Provider.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.

Prop
Type
Default
containerUnion—
A parent element to render the portal element into.HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)
Portal.State
type ToastPortalState = {};

Viewport

A container viewport for toasts. Renders a <div> element.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-expandedboolean—
Indicates toasts are expanded in the viewport.boolean
Attribute
Description
data-expanded
Indicates toasts are expanded in the viewport.

CSS variables

Name
Type
Default
--toast-frontmost-heightnumber—
Indicates the height of the frontmost toast.number
CSS 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.

Prop
Type
Default
swipeDirectionUnion['down', 'right']
Direction(s) in which the toast can be swiped to dismiss.'up' | 'down' | 'left' | 'right' | ('left' | 'right' | 'up' | 'down')[]
toast*Toast.Root.ToastObject—
The toast to render.Toast.Root.ToastObject
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-expandedboolean—
Present when the toast is expanded in the viewport.boolean
data-limitedboolean—
Present when the toast was limited because the toast limit was exceeded.boolean
data-swipe-directionUnion—
The direction the toast was swiped.'up' | 'down' | 'left' | 'right'
data-swipingboolean—
Present when the toast is being swiped.boolean
data-typestring—
The type of the toast.string
data-starting-style-—
Present when the toast begins animating in.-
data-ending-style-—
Present when the toast is animating out.-
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

Name
Type
Default
--toast-heightnumber—
Indicates the measured natural height of the toast in pixels.number
--toast-indexnumber—
Indicates the index of the toast in the list.number
--toast-offset-ynumber—
Indicates the vertical pixels offset of the toast in the list when expanded.number
--toast-swipe-movement-xnumber—
Indicates the horizontal swipe movement of the toast.number
--toast-swipe-movement-ynumber—
Indicates the vertical swipe movement of the toast.number
CSS 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.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-behindboolean—
Present when the toast is behind the frontmost toast in the stack.boolean
data-expandedboolean—
Present when the toast viewport is expanded.boolean
Attribute
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.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-typestring—
The type of the toast.string
Attribute
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.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-typestring—
The type of the toast.string
Attribute
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.

Prop
Type
Default
nativeButtonbooleantrue
Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).boolean
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-typestring—
The type of the toast.string
Attribute
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.

Prop
Type
Default
nativeButtonbooleantrue
Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).boolean
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-typestring—
The type of the toast.string
Attribute
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.

Prop
Type
Default
disableAnchorTrackingbooleanfalse
Whether to disable the popup from tracking any layout shift of its positioning anchor.boolean
toast*ToastObject<any>—
The toast object associated with the positioner.ToastObject<any>
alignAlign'center'
How to align the popup relative to the specified side.Align
alignOffsetUnion0
Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a 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 | OffsetFunction
sideSide'top'
Which side of the anchor element to align the toast against. May automatically change to avoid collisions.Side
sideOffsetUnion0
Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a 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 | OffsetFunction
arrowPaddingnumber5
Minimum distance to maintain between the arrow and the edges of the popup. Use it to prevent the arrow element from hanging out of the rounded corners of a popup.number
anchorUnion—
An element to position the toast against.Element | null
collisionAvoidanceCollisionAvoidance—
Determines how to handle collisions when positioning the popup. 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'.CollisionAvoidance
collisionBoundaryBoundary'clipping-ancestors'
An element or a rectangle that delimits the area that the popup is confined to.Boundary
collisionPaddingPadding5
Additional space to maintain from the edge of the collision boundary.Padding
stickybooleanfalse
Whether to maintain the popup in the viewport after the anchor element was scrolled out of view.boolean
positionMethodUnion'absolute'
Determines which CSS position property to use.'absolute' | 'fixed'
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-anchor-hidden-—
Present when the anchor is hidden.-
data-alignUnion—
Indicates how the toast is aligned relative to specified side.'start' | 'center' | 'end'
data-sideUnion—
Indicates which side the toast is positioned relative to the trigger.'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

Name
Type
Default
--anchor-heightnumber—
The anchor’s height.number
--anchor-widthnumber—
The anchor’s width.number
--available-heightnumber—
The available height between the anchor and the edge of the viewport.number
--available-widthnumber—
The available width between the anchor and the edge of the viewport.number
--transform-originstring—
The coordinates that this element is anchored to. Used for animations and transitions.string
CSS 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.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-uncentered-—
Present when the toast arrow is uncentered.-
data-alignUnion—
Indicates how the toast is aligned relative to specified side.'start' | 'center' | 'end'
data-sideUnion—
Indicates which side the toast is positioned relative to the anchor.'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>.

Usage
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.

Usage
const toastId = toastManager.add({
  description: 'Hello, world!',
});
Example
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.

Usage
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:

Deriving the update from the current toast
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.

Usage
toastManager.close(toastId);

Or you can close all toasts at once by not passing an ID:

Close all toasts
toastManager.close();

promise method

Creates an asynchronous toast with three possible states: loading, success, and error.

Description configuration
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:

Method options configuration
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;
};