A lightweight, chainable notification system for React with smooth animations, flexible theming, and a Sonner-like API.
- Chainable API: State transitions made easy (
notify.loading().success()) - Promise Tracking: Automatic state updates for async operations
- Confirmations: Built-in async/await confirmation dialogs
- Highly Customizable: 6 positions, 3 radius styles, and dark/light/auto modes
- Performance First: Driven by Framer Motion for buttery smooth 60fps animations
- Swipe to Dismiss: Natural touch and mouse gestures with direction awareness
- Lightweight: Zero unnecessary dependencies, tree-shakable
- Typescript: Core-first TS support with rich type definitions
bun add @remcostoeten/notifier
# or
npm install @remcostoeten/notifier- Add the Notifier component to your root layout:
import { Notifier } from '@remcostoeten/notifier'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Notifier position='bottom-right' />
</body>
</html>
)
}- Call notify from anywhere:
import { notify } from '@remcostoeten/notifier'
function MyComponent() {
const handleSave = async () => {
const n = notify.loading('Saving...')
try {
await saveData()
n.success('Saved!')
} catch (err) {
n.error('Failed to save')
}
}
return <button onClick={handleSave}>Save</button>
}notify('Message') // Info notification
notify.loading('Processing...') // Loading state
notify.success('Done!') // Success state
notify.error('Failed') // Error state
notify.dismiss() // Dismiss all
notify.dismiss(id) // Dismiss by IDEach method returns an instance for state transitions:
const n = notify.loading('Saving...')
try {
await saveData()
n.success('Saved!')
} catch (err) {
n.error(err.message)
}notify.promise(fetchData(), {
loading: 'Loading...',
success: 'Data loaded!',
error: (err) => `Error: ${err.message}`
})const confirmed = await notify.confirm('Delete this item?', {
confirmLabel: 'Delete',
cancelLabel: 'Cancel'
})
if (confirmed) {
await deleteItem()
}| Prop | Type | Default | Description |
|---|---|---|---|
| position | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' |
'bottom-right' |
Notification position |
| duration | number |
3000 |
Auto-dismiss duration in ms. Set to 0 to disable |
| maxVisible | number |
5 |
Maximum visible notifications |
| colorMode | 'dark' | 'light' | 'auto' |
'dark' |
Color theme |
| radius | 'pill' | 'rounded' | 'squared' |
'pill' |
Border radius style |
| iconColor | 'colored' | 'neutral' | 'hidden' |
'colored' |
Icon color mode |
| swipeToDismiss | boolean |
true |
Enable swipe gestures |
| pauseOnHover | boolean |
true |
Pause auto-dismiss on hover |
| gap | number |
8 |
Gap between notifications (px) |
├── packages/
│ ├── notifier/ # Core library (@remcostoeten/notifier)
│ └── release-cli/ # Internal release automation tool
├── apps/
│ └── demo/ # Documentation & Showcase (Next.js)
└── README.md
# Install dependencies
bun install
# Run documentation/showcase
bun dev
# Build the library
bun run build:notifierMIT © Remco Stoeten
