A classic, toast notification library for React and Next.js.
Dark & light mode ready. Fully themeable. Zero config to get started.
| Dark Mode | Light Mode |
|---|---|
|
|
- 4 variants —
default,success,destructive,loading toast.promise()— auto loading → success/error lifecycletoast.custom()— render any React node as a toasttoast.update()— mutate an existing toast in placetoast.dismiss()— programmatically remove a toast- Pause on hover — timer pauses when mouse is over the toast
- Swipe to dismiss — horizontal swipe on touch devices
- Action button — inline CTA with async support
- Custom icon — replace the dot/spinner with any React node
maxToasts— cap the number of visible toaststoastClassName/containerClassName— BYO CSS classesclassNameper toast — fine-grained styling- Dark & light mode out of the box
- Responsive — auto centers on mobile (≤768px)
- Smooth per-position enter + fade-out exit animations
- Respects
prefers-reduced-motion - Fully customizable colors, background, border, shadow, and duration
- Lightweight — no extra dependencies beyond React
- Works with React 17+ and Next.js (App & Pages Router)
- TypeScript first — all types exported
npm i @iryanraushan/notchifyPeer dependencies:
npm install react react-dom
1. Wrap your app with ToastProvider
// App.tsx or layout.tsx (Next.js)
import { ToastProvider } from '@iryanraushan/notchify';
export default function RootLayout({ children }) {
return (
<ToastProvider>
{children}
</ToastProvider>
);
}2. Trigger toasts from anywhere
import { useToast } from '@iryanraushan/notchify';
export default function MyComponent() {
const { toast } = useToast();
return (
<button onClick={() => toast({ message: 'Saved!', variant: 'success' })}>
Save
</button>
);
}| Variant | Dot Color | Usage |
|---|---|---|
default |
Blue #3b82f6 |
General info |
success |
Green #22c55e |
Operation succeeded |
destructive |
Red #ef4444 |
Error / failure |
loading |
Purple #a855f7 |
In-progress, persists until updated |
const { toast } = useToast();
toast({ message: 'Profile updated.' });
toast({ message: 'Payment successful!', variant: 'success' });
toast({ message: 'Failed to delete file.', variant: 'destructive' });
toast.loading('Uploading…');Shorthand methods:
toast.success('Done!');
toast.error('Something went wrong.');
const id = toast.loading('Please wait…');Automatically shows a loading toast, then transitions to success or error.
toast.promise accepts either a Promise directly or a factory function () => Promise<T>.
await toast.promise(fetch('/api/save'), {
loading: 'Saving…',
success: 'Saved!',
error: 'Failed to save.',
});
// factory function form
await toast.promise(() => fetchUser(), {
loading: 'Loading user…',
success: 'Loaded!',
error: 'Failed.',
});
// Dynamic messages based on result / error
await toast.promise(fetchUser(), {
loading: 'Loading user…',
success: (user) => `Welcome, ${user.name}!`,
error: (err) => `Error: ${err.message}`,
duration: 4000, // duration for the success/error state
});Mutate any field of an existing toast by its id.
const id = toast.loading('Uploading…');
// later…
toast.update(id, { variant: 'success', message: 'Upload complete!' });const { toast, dismiss } = useToast();
// via the API object
const id = toast.loading('Working…');
toast.dismiss(id);
// via the context shorthand
dismiss(id);// per toast (ms)
toast({ message: 'Stays 6 s.', duration: 6000 });
// globally via theme
<ToastProvider theme={{ defaultDuration: 5000 }}>An inline CTA rendered inside the toast. onClick can be async — the button shows a loading state while the promise resolves.
toast({
message: 'File deleted.',
action: {
label: 'Undo',
onClick: async () => {
await restoreFile();
},
},
});Replace the colored dot with any React node.
toast({
message: 'New message.',
icon: <MailIcon size={16} />,
});Render a fully custom React component. All standard toast behaviour (auto-dismiss, hover-pause, swipe) still applies.
toast.custom(
<div style={{ padding: 12, background: '#1e1e2e', borderRadius: 12, color: '#fff' }}>
<strong>Custom UI</strong>
<p>Anything goes here.</p>
</div>
);
// with extra options
toast.custom(<MyCard />, { duration: 5000, id: 'my-card' });The auto-dismiss timer automatically pauses when the mouse is over a toast and resumes when it leaves. No configuration needed.
On touch devices, swiping a toast horizontally by more than 80 px dismisses it. Built-in, no configuration needed.
<ToastProvider mode="dark"> {/* default */}
<ToastProvider mode="light">Default values per mode:
| Token | Dark | Light |
|---|---|---|
background |
#0c0b0b |
#ffffff |
border |
#1a1a1a |
#e5e5e5 |
textColor |
#f5f5f5 |
#111111 |
boxShadow |
subtle dark shadow | subtle light shadow |
<ToastProvider position="top-right">| Value | Description |
|---|---|
top-left |
Top-left corner |
top-center |
Top center |
top-right |
Top-right corner |
bottom-left |
Bottom-left corner |
bottom-center |
Bottom center |
bottom-right (default) |
Bottom-right corner |
Any top-* position → top-center, any bottom-* → bottom-center. Toasts are horizontally centered and never pinned to the edge.
Cap how many toasts are shown at once. Oldest toasts are dropped when the limit is exceeded.
<ToastProvider maxToasts={3}>Override any visual token. Unset values fall back to the mode defaults.
<ToastProvider
mode="dark"
theme={{
background: '#1a1a2e',
border: '#2a2a4a',
textColor: '#e0e0ff',
boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
defaultDuration: 5000,
dotColors: {
default: '#6366f1',
success: '#00ff88',
destructive: '#ff4466',
loading: '#facc15',
},
}}
>Apply Tailwind (or any CSS) classes globally or per toast.
// global — applied to every toast
<ToastProvider toastClassName="my-toast" containerClassName="my-container">
// per toast
toast({ message: 'Hi', className: 'border-blue-500' });Toasts animate in based on their position and fade + scale out on dismiss.
| Position | Enter animation |
|---|---|
top-left / bottom-left |
Slide in from left |
top-right / bottom-right |
Slide in from right |
top-center |
Slide in from top |
bottom-center |
Slide in from bottom |
| Exit (all) | Fade out + scale down |
Animations are automatically disabled when prefers-reduced-motion: reduce is set.
- Toast container has
role="region"andaria-label="Notifications" - Each toast has
role="status"andaria-live="polite"/aria-atomic="true" - The dot/dismiss button has
aria-label="Dismiss" - Action button carries
aria-labelfrom the label text
// components/providers.tsx
'use client';
import { ToastProvider } from '@iryanraushan/notchify';
export function Providers({ children }: { children: React.ReactNode }) {
return <ToastProvider mode="dark" position="bottom-right">{children}</ToastProvider>;
}// app/layout.tsx
import { Providers } from '@/components/providers';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Toast and ToastViewport are exported as low-level components for advanced / headless use cases where you want to render toasts outside of ToastProvider.
import { Toast, ToastViewport } from '@iryanraushan/notchify';| Component | Description |
|---|---|
Toast |
Single toast item — accepts all ToastProps including theme, mode, style |
ToastViewport |
The fixed positioned container that wraps toasts |
Both components read from ToastContext when available, and fall back to their own props otherwise.
| Prop | Type | Default | Description |
|---|---|---|---|
mode |
"dark" | "light" |
"dark" |
Color mode |
position |
ToastPosition |
"bottom-right" |
Toast position on desktop |
theme |
ToastTheme |
{} |
Visual overrides |
maxToasts |
number |
— | Max toasts visible at once |
toastClassName |
string |
— | Class applied to every toast |
containerClassName |
string |
— | Class applied to the viewport container |
children |
ReactNode |
— | Your app |
| Key | Type | Description |
|---|---|---|
background |
string |
Toast background color |
border |
string |
Toast border color |
textColor |
string |
Message text color |
boxShadow |
string |
Toast shadow |
defaultDuration |
number |
Default auto-dismiss duration in ms |
dotColors |
{ default?, success?, destructive?, loading? } |
Dot color per variant |
const { toast, dismiss, theme, mode, position } = useToast();| Key | Type | Description |
|---|---|---|
toast |
ToastAPI |
Full toast API (see below) |
dismiss(id) |
function |
Remove a toast by id |
theme |
ToastTheme |
Resolved theme object |
mode |
ToastMode |
Current mode |
position |
ToastPosition |
Current position |
| Method | Signature | Description |
|---|---|---|
toast(options) |
(options) => string |
Show a toast, returns id |
Note:
toast.promiseacceptsPromise<T>or() => Promise<T>as its first argument. |toast.success(msg, opts?)|(string, opts?) => string| Success variant shorthand | |toast.error(msg, opts?)|(string, opts?) => string| Destructive variant shorthand | |toast.loading(msg, opts?)|(string, opts?) => string| Loading variant, persists | |toast.custom(node, opts?)|(ReactNode, opts?) => string| Fully custom UI | |toast.promise(p, msgs)|(Promise, msgs) => Promise| Loading → success/error lifecycle | |toast.update(id, opts)|(string, Partial<ToastItem>) => void| Mutate an existing toast | |toast.dismiss(id)|(string) => void| Remove a toast by id |
| Option | Type | Default | Description |
|---|---|---|---|
message |
string |
— | Notification text (required for standard toasts) |
variant |
"default" | "success" | "destructive" | "loading" |
"default" |
Visual style |
type |
ToastVariant |
— | Deprecated — use variant instead |
duration |
number |
3000 |
Auto-dismiss time in ms |
id |
string |
auto | Custom id (useful for update) |
icon |
ReactNode |
— | Custom icon replaces the dot |
action |
ToastAction |
— | Inline action button |
className |
string |
— | Extra CSS class for this toast |
custom |
ReactNode |
— | Fully custom component |
| Key | Type | Description |
|---|---|---|
label |
ReactNode |
Button label |
onClick |
() => void | Promise<void> |
Click handler (async supported) |
| Key | Type | Description |
|---|---|---|
loading |
string |
Message shown while pending |
success |
string | (data: T) => string |
Message (or factory) on resolve |
error |
string | (err: unknown) => string |
Message (or factory) on reject |
duration |
number |
Duration for the resolved/rejected state |
import type {
ToastOptions,
ToastItem,
ToastVariant,
ToastMode,
ToastPosition,
ToastTheme,
ToastAction,
ToastAPI,
ToastPromiseMessages,
ToastContextValue,
ToastProviderProps,
} from '@iryanraushan/notchify';MIT © Raushan Kumar

