Toast
A portaled, auto-dismissing notification queued through a manager hook.
Basic
Show codeHide code
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { ToastProvider, useToast } from "@makeplane/propel/components/toast";
import { X } from "lucide-react";
function ToastTrigger() {
const { add } = useToast();
return (
<Button
sizing="hug"
prominence="secondary"
tone="neutral"
magnitude="md"
label="Show notification"
onClick={() =>
add({
title: "Project created",
description: "Marketing site is ready for your team.",
data: { tone: "success" },
})
}
/>
);
}
export default function BasicDemo() {
return (
<ToastProvider
close={
<IconButton
prominence="ghost"
tone="neutral"
magnitude="sm"
aria-label="Dismiss"
icon={<Icon icon={X} />}
/>
}
>
<ToastTrigger />
</ToastProvider>
);
}Tones
The required tone in a toast’s data selects its status icon and color: success, danger, info, warning, or neutral.
Show codeHide code
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { type ToastTone, ToastProvider, useToast } from "@makeplane/propel/components/toast";
import { X } from "lucide-react";
const TONES: { tone: ToastTone; label: string; title: string; description: string }[] = [
{
tone: "success",
label: "Success",
title: "Project created",
description: "Marketing site is ready for your team.",
},
{
tone: "danger",
label: "Danger",
title: "Remove failed",
description: "Could not remove the workspace member.",
},
{
tone: "info",
label: "Info",
title: "Import in progress",
description: "We're syncing your issues from GitHub.",
},
{
tone: "warning",
label: "Warning",
title: "Storage almost full",
description: "You've used 90% of your workspace storage.",
},
{
tone: "neutral",
label: "Neutral",
title: "Draft saved",
description: "Your changes are stored locally.",
},
];
function ToneTriggers() {
const { add } = useToast();
return (
<div className="flex flex-wrap items-center gap-3">
{TONES.map(({ tone, label, title, description }) => (
<Button
key={tone}
sizing="hug"
prominence="secondary"
tone="neutral"
magnitude="md"
label={label}
onClick={() => add({ title, description, data: { tone } })}
/>
))}
</div>
);
}
export default function TonesDemo() {
return (
<ToastProvider
close={
<IconButton
prominence="ghost"
tone="neutral"
magnitude="sm"
aria-label="Dismiss"
icon={<Icon icon={X} />}
/>
}
>
<ToneTriggers />
</ToastProvider>
);
}With actions
Pass actions for a left-aligned cluster of up to two buttons, and primaryAction for a right-aligned button.
Show codeHide code
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { ToastProvider, useToast } from "@makeplane/propel/components/toast";
import { X } from "lucide-react";
function ActionTrigger() {
const { add } = useToast();
return (
<Button
sizing="hug"
prominence="secondary"
tone="neutral"
magnitude="md"
label="Mark as done"
onClick={() =>
add({
title: "Issue moved to Done",
description: "PROJ-142 was marked complete.",
data: {
tone: "success",
actions: [{ label: "Undo" }],
primaryAction: { label: "View" },
},
})
}
/>
);
}
export default function WithActionsDemo() {
return (
<ToastProvider
close={
<IconButton
prominence="ghost"
tone="neutral"
magnitude="sm"
aria-label="Dismiss"
icon={<Icon icon={X} />}
/>
}
>
<ActionTrigger />
</ToastProvider>
);
}With progress
Set progress (0–100) to report a long-running task; a thin bar renders between the description and the action row.
Show codeHide code
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { ToastProvider, useToast } from "@makeplane/propel/components/toast";
import { X } from "lucide-react";
function ProgressTrigger() {
const { add } = useToast();
return (
<Button
sizing="hug"
prominence="secondary"
tone="neutral"
magnitude="md"
label="Export workspace"
onClick={() =>
add({
title: "Exporting workspace",
description: "Preparing your data for download.",
data: {
tone: "info",
progress: 32,
actions: [{ label: "Cancel" }],
},
})
}
/>
);
}
export default function WithProgressDemo() {
return (
<ToastProvider
close={
<IconButton
prominence="ghost"
tone="neutral"
magnitude="sm"
aria-label="Dismiss"
icon={<Icon icon={X} />}
/>
}
>
<ProgressTrigger />
</ToastProvider>
);
}Promise
useToast().promise(promise, { loading, success, error }) queues a loading toast, then updates it in place when the promise settles.
Show codeHide code
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { type ToastData, ToastProvider, useToast } from "@makeplane/propel/components/toast";
import { X } from "lucide-react";
function PromiseTrigger() {
const { promise } = useToast();
return (
<Button
sizing="hug"
prominence="secondary"
tone="neutral"
magnitude="md"
label="Upload files"
onClick={() =>
void promise<string, ToastData>(
new Promise<string>((resolve) => {
setTimeout(() => resolve("3 attachments"), 1500);
}),
{
loading: {
title: "Uploading files",
description: "Your attachments are on their way.",
data: { tone: "neutral" },
},
success: (uploaded) => ({
title: "Upload complete",
description: `${uploaded} uploaded.`,
data: { tone: "success" },
}),
error: {
title: "Upload failed",
description: "Something went wrong — try again.",
data: { tone: "danger" },
},
},
)
}
/>
);
}
export default function PromiseDemo() {
return (
<ToastProvider
close={
<IconButton
prominence="ghost"
tone="neutral"
magnitude="sm"
aria-label="Dismiss"
icon={<Icon icon={X} />}
/>
}
>
<PromiseTrigger />
</ToastProvider>
);
}Installation
import { ToastProvider, useToast } from "@makeplane/propel/components/toast";
Props
ToastProvider
| Prop | Type | Required | Description |
|---|---|---|---|
| toastManager | ToastManager<ToastData> | No | Optional external manager. Must queue Propel `ToastData` so every toast has a `tone`. |
| close | ReactElement<unknown, string | JSXElementConstructor<any>> | Yes | The close control (e.g. an `IconButton`) rendered as each toast's close button. It carries its own — localizable — `aria-label`; the toast bakes no label or glyph. |
Toast
| Prop | Type | Required | Description |
|---|---|---|---|
| render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, ToastRootState> | No | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| close | ReactElement<unknown, string | JSXElementConstructor<any>> | Yes | The close control (e.g. an `IconButton`), rendered as the toast's close button. It carries its own — localizable — `aria-label`; the toast bakes no label or glyph. |