Installation
Complete project setup first. Your configuration
must select react/base and include your Design System ID.
pnpm exec frontfriend download toast -f react --primitive baseReview dependencies reported by the downloader and keep your source changes in version control.
Usage
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="neutral" title="Saved" description="Your changes were saved." action={false} cancel={false} />
</ToastProvider>
);
} Examples
Default
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="neutral" title="Title" description="Description" action cancel />
</ToastProvider>
);
} With Title Description
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="neutral" title="Event created" description="Friday, February 10, 2023 at 5:57 PM" action={false} cancel={false} />
</ToastProvider>
);
} With Action
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="neutral" title="Title" description="Description" action cancel />
</ToastProvider>
);
} Destructive
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="destructive" title="Something went wrong" description="Please try again." action cancel={false} />
</ToastProvider>
);
} Success
import {
createToastManager,
ToastProvider,
useToastManager,
ToastPortal,
ToastViewport,
Toast,
ToastContent,
ToastTitle,
ToastDescription,
ToastAction,
ToastClose,
} from "@/components/ui/toast";
import { Button } from "@/components/ui/button";
import { cn } from "@frontfriend/sdk/lib/react/utils";
import { ffdc, config } from "@frontfriend/sdk";
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
import * as React from "react";
type DemoProps = {
title?: string;
description?: string;
action?: boolean;
cancel?: boolean;
variant?: "neutral" | "destructive" | "info" | "warning" | "success";
};
const styling = ffdc(config || {}).toast;
function Notifications({
title,
description,
action,
cancel,
variant = "neutral",
}: DemoProps) {
const { toasts, add } = useToastManager<{
cancel?: boolean;
}>();
return (
<>
<Button
variant="tertiary"
onClick={() =>
add({
title,
description,
type: variant === "destructive" ? "error" : variant,
actionProps: action ? { children: "Action" } : undefined,
data: { cancel },
})
}
>
Show Toast
</Button>
<ToastPortal>
<ToastViewport>
{toasts.map((item) => (
<Toast key={item.id} toast={item} variant={variant}>
<ToastContent>
<div className={cn(styling.slots.content)}>
<ToastTitle />
<ToastDescription />
</div>
<ToastAction />
{item.data?.cancel && (
<ToastPrimitive.Close
data-slot="toast-cancel"
render={<Button variant="tertiary" size="sm" />}
>
Undo
</ToastPrimitive.Close>
)}
<ToastClose />
</ToastContent>
</Toast>
))}
</ToastViewport>
</ToastPortal>
</>
);
}
export default function ToastExample() {
const [manager] = React.useState(() =>
createToastManager<{
cancel?: boolean;
}>(),
);
return (
<ToastProvider toastManager={manager}>
<Notifications variant="success" title="Saved" description="Your changes were saved." action={false} cancel={false} />
</ToastProvider>
);
} API reference
Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Base UI.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "neutral" | "destructive" | "warning" | "success" | "info" | null | — |
Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.