ComponentsFeedback

Toast

A short note, bottom right, that something just happened: a save, a move, an archive. It states the fact in the past tense and offers Undo when the action can be reversed.

Playground

Change the controls to try each option. The preview holds the toast open; on a page a toast closes after 5 seconds, and an error stays until closed. The code under the preview is the call to copy.

import { toast } from '@/ui/toast';toast.add({  type: "success",  title: "Company added",});

Types

Press a button to show its toast, bottom right. Success confirms a save the person can't see change. Error says what failed and what to do, and stays until closed. Information is a neutral fact; warning is a result with a catch.

import { Button } from '@/ui/button';
import { toast } from '@/ui/toast';

export default function ToastTypes() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-100">
      <Button onClick={() => toast.add({ type: 'success', title: 'Company added' })}>Success</Button>
      <Button
        onClick={() =>
          toast.add({ type: 'error', title: 'Stage not saved', description: 'Check your connection and try again.' })
        }
      >
        Error
      </Button>
      <Button onClick={() => toast.add({ type: 'info', title: 'Link copied' })}>Information</Button>
      <Button onClick={() => toast.add({ type: 'warning', title: 'Saved, but 3 rows were skipped' })}>Warning</Button>
    </div>
  );
}

Undo

A reversible action happens at once and offers Undo. The button shows ⌘Z (Ctrl+Z on Windows and Linux), and that shortcut presses the newest Undo from anywhere outside a text field.

import { ArchiveIcon } from '@phosphor-icons/react';
import { Button } from '@/ui/button';
import { toast } from '@/ui/toast';

export default function ToastUndo() {
  function archive() {
    // Archive at once, then offer the way back. ⌘Z presses the newest Undo.
    const id = toast.add({
      type: 'success',
      title: 'Halcyon Bank archived',
      actionProps: {
        children: 'Undo',
        onClick: () => {
          toast.close(id);
          toast.add({ type: 'success', title: 'Halcyon Bank restored' });
        },
      },
    });
  }
  return (
    <Button onClick={archive}>
      <ArchiveIcon />
      Archive company
    </Button>
  );
}

Loading

For work that takes a while, toast.promise shows a loading toast that turns into the success or error toast in place.

import { UploadSimpleIcon } from '@phosphor-icons/react';
import { Button } from '@/ui/button';
import { toast } from '@/ui/toast';

const importCompanies = () => new Promise((resolve) => setTimeout(resolve, 2000));

export default function ToastLoading() {
  return (
    <Button
      onClick={() =>
        toast.promise(importCompanies(), {
          loading: { title: 'Importing 240 companies' },
          success: { title: '240 companies imported' },
          error: { title: 'Import failed', description: 'Nothing was added. Check the file and try again.' },
        })
      }
    >
      <UploadSimpleIcon />
      Import companies
    </Button>
  );
}

On this page