An opinionated toast component for React providing comprehensive toast notifications with customization options
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Sonner is an opinionated toast component for React that provides a comprehensive solution for displaying toast notifications. It offers a simple and intuitive API through a Toaster component and toast function, supporting various toast types including success, error, info, warning, loading, and custom toasts with actions.
npm install sonnerimport { Toaster, toast } from "sonner";For CommonJS:
const { Toaster, toast } = require("sonner");Additional imports for advanced usage:
import {
Toaster,
toast,
useSonner,
type ExternalToast,
type ToasterProps,
type ToastT
} from "sonner";import React from "react";
import { Toaster, toast } from "sonner";
function App() {
return (
<div>
{/* Add Toaster component once in your app */}
<Toaster />
{/* Create toasts from anywhere */}
<button onClick={() => toast("Hello World!")}>
Show Toast
</button>
<button onClick={() => toast.success("Success!")}>
Success Toast
</button>
<button onClick={() => toast.error("Something went wrong!")}>
Error Toast
</button>
</div>
);
}Sonner is built around several key components:
Primary toast creation functions for displaying notifications with different visual styles and behaviors.
function toast(message: string | React.ReactNode, options?: ExternalToast): string | number;
// Type-specific toast functions
namespace toast {
function success(message: string | React.ReactNode, options?: ExternalToast): string | number;
function error(message: string | React.ReactNode, options?: ExternalToast): string | number;
function info(message: string | React.ReactNode, options?: ExternalToast): string | number;
function warning(message: string | React.ReactNode, options?: ExternalToast): string | number;
function loading(message: string | React.ReactNode, options?: ExternalToast): string | number;
function message(message: string | React.ReactNode, options?: ExternalToast): string | number;
function dismiss(id?: string | number): string | number | undefined;
function promise<T>(promise: PromiseT<T>, options: PromiseData<T>): { unwrap(): Promise<T> } & (string | number);
function custom(jsx: (id: string | number) => React.ReactElement, options?: ExternalToast): string | number;
function getHistory(): ToastT[];
function getToasts(): ToastT[];
}The main React component that renders and manages all toast notifications with comprehensive configuration options.
const Toaster: React.ForwardRefExoticComponent<ToasterProps & React.RefAttributes<HTMLElement>>;
interface ToasterProps {
invert?: boolean;
theme?: "light" | "dark" | "system";
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center";
hotkey?: string[];
richColors?: boolean;
expand?: boolean;
duration?: number;
gap?: number;
visibleToasts?: number;
closeButton?: boolean;
toastOptions?: ToastOptions;
className?: string;
style?: React.CSSProperties;
offset?: Offset;
mobileOffset?: Offset;
dir?: "rtl" | "ltr" | "auto";
swipeDirections?: SwipeDirection[];
/** @deprecated Use icons prop instead */
loadingIcon?: React.ReactNode;
icons?: ToastIcons;
containerAriaLabel?: string;
pauseWhenPageIsHidden?: boolean;
}Advanced toast functionality including promise integration, custom JSX content, and action buttons.
// Promise-based toasts
function toast.promise<T>(
promise: Promise<T> | (() => Promise<T>),
options: PromiseData<T>
): { unwrap(): Promise<T> } & (string | number);
// Custom JSX toasts
function toast.custom(
jsx: (id: string | number) => React.ReactElement,
options?: ExternalToast
): string | number;
// Toast state management
function toast.getHistory(): ToastT[];
function toast.getToasts(): ToastT[];React hooks for integrating with toast state and managing toast notifications programmatically.
function useSonner(): {
toasts: ToastT[];
};interface ExternalToast {
id?: string | number;
type?: "normal" | "action" | "success" | "info" | "warning" | "error" | "loading" | "default";
icon?: React.ReactNode;
jsx?: React.ReactNode;
richColors?: boolean;
invert?: boolean;
closeButton?: boolean;
dismissible?: boolean;
description?: (() => React.ReactNode) | React.ReactNode;
duration?: number;
action?: Action | React.ReactNode;
cancel?: Action | React.ReactNode;
onDismiss?: (toast: ToastT) => void;
onAutoClose?: (toast: ToastT) => void;
cancelButtonStyle?: React.CSSProperties;
actionButtonStyle?: React.CSSProperties;
style?: React.CSSProperties;
unstyled?: boolean;
className?: string;
classNames?: ToastClassnames;
descriptionClassName?: string;
position?: Position;
}
interface Action {
label: React.ReactNode;
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
actionButtonStyle?: React.CSSProperties;
}
interface ToastT {
id: number | string;
title?: (() => React.ReactNode) | React.ReactNode;
type?: "normal" | "action" | "success" | "info" | "warning" | "error" | "loading" | "default";
icon?: React.ReactNode;
jsx?: React.ReactNode;
richColors?: boolean;
invert?: boolean;
closeButton?: boolean;
dismissible?: boolean;
description?: (() => React.ReactNode) | React.ReactNode;
duration?: number;
delete?: boolean;
action?: Action | React.ReactNode;
cancel?: Action | React.ReactNode;
onDismiss?: (toast: ToastT) => void;
onAutoClose?: (toast: ToastT) => void;
promise?: Promise<any> | (() => Promise<any>);
cancelButtonStyle?: React.CSSProperties;
actionButtonStyle?: React.CSSProperties;
style?: React.CSSProperties;
unstyled?: boolean;
className?: string;
classNames?: ToastClassnames;
descriptionClassName?: string;
position?: Position;
}
type Position = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center";
type SwipeDirection = "top" | "right" | "bottom" | "left";
interface ToastClassnames {
toast?: string;
title?: string;
description?: string;
loader?: string;
closeButton?: string;
cancelButton?: string;
actionButton?: string;
success?: string;
error?: string;
info?: string;
warning?: string;
loading?: string;
default?: string;
content?: string;
icon?: string;
}
interface ToastIcons {
success?: React.ReactNode;
info?: React.ReactNode;
warning?: React.ReactNode;
error?: React.ReactNode;
loading?: React.ReactNode;
close?: React.ReactNode;
}
type Offset = {
top?: string | number;
right?: string | number;
bottom?: string | number;
left?: string | number;
} | string | number;
interface PromiseData<ToastData = any> {
loading?: string | React.ReactNode;
success?: string | React.ReactNode | ((data: ToastData) => React.ReactNode | string | Promise<React.ReactNode | string>);
error?: string | React.ReactNode | ((error: any) => React.ReactNode | string | Promise<React.ReactNode | string>);
description?: string | React.ReactNode | ((data: any) => React.ReactNode | string | Promise<React.ReactNode | string>);
finally?: () => void | Promise<void>;
}
type PromiseT<Data = any> = Promise<Data> | (() => Promise<Data>);
type PromiseExternalToast = Omit<ExternalToast, 'description'>;