AZMARA PLATFORM
Recipes

Show a toast while a promise runs

Report the loading, success and failure of one async call with toast.promise.

View as Markdown for coding assistants, or see llms.txt.

Pass the promise to toast.promise with a message for each outcome. One toast moves from loading to success or error, so nothing stacks up.

Preview theme
Preview density
"use client";

import { Button } from "@azmr/ui/button";
import { toast } from "@azmr/ui/toast";

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

export default function ToastPromise() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button
        variant="outline"
        onClick={() =>
          toast.promise(
            wait(1500).then(() => 12),
            {
              loading: "Exporting…",
              success: (rows) => `Exported ${rows} rows`,
              error: "Export failed",
            },
          )
        }
      >
        Export (succeeds)
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast
            .promise(
              wait(1500).then(() => Promise.reject(new Error("Disk full"))),
              {
                loading: "Exporting…",
                success: "Exported",
                error: (e: Error) => `Export failed: ${e.message}`,
              },
            )
            .catch(() => {})
        }
      >
        Export (fails)
      </Button>
    </div>
  );
}

The pattern

import { toast } from "@azmr/ui/toast";

toast.promise(save(), {
  loading: "Saving…",
  success: (rows) => `Saved ${rows} rows`,
  error: (e: Error) => `Could not save: ${e.message}`,
});
  • success and error can be a string or a function of the resolved value or the error.
  • A rejected promise is still rejected for you: toast.promise returns the promise, so catch it (.catch(() => {})) if you have nothing more to do, as the failing button above does.
  • Toasts are announced by a polite live region and do not move focus, so the person keeps typing.
  • Mount one Toaster near the root of the app. Toasts show for a few seconds, so use one only for something that needs no decision. If the person has to choose, use a dialog.

For a toast with an action, such as Undo, see the confirm-delete example.

On this page