A lightweight, performant notification library for React applications.
- ⚡ Lightning Fast - Optimized performance with minimal re-renders
- 📦 Tiny Bundle - Only ~3.5KB gzipped, zero dependencies
- 🎨 Fully Customizable - Complete control over styling and animations
- 🔧 TypeScript First - Built with TypeScript, full type safety
- 🌐 SSR Compatible - Works with Next.js, Remix, and other SSR frameworks
- ⏸️ Pause on Hover - Auto-dismiss timer pauses when users hover
- 🎯 Smart Positioning - 6 built-in positions with stack management
npm install react-simple-notifyyarn add react-simple-notifypnpm add react-simple-notifyimport { notify, NotifyContainers } from 'react-simple-notify';
function App() {
const showNotification = () => {
notify.open({
render: ({ onClose }) => (
<div className="notification">
<h4>Success!</h4>
<p>Your changes have been saved</p>
<button onClick={onClose}>Close</button>
</div>
),
});
};
return (
<>
<button onClick={showNotification}>Show Notification</button>
<NotifyContainers />
</>
);
}Opens a new notification.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
render |
(args: NotifyRenderArgs) => ReactNode |
Yes | - | Render function that returns notification content |
id |
string |
No | Auto-generated | Unique identifier for the notification |
duration |
number |
No | 3500 |
Time in ms before auto-close. Set to 0 for persistent |
alignment |
NotifyAlignment |
No | bottomLeft |
Position where notification appears |
pauseOnHover |
boolean |
No | false |
Pause auto-dismiss timer on hover |
data |
any |
No | undefined |
Custom data passed to render function |
dataTestId |
string |
No | undefined |
Test ID for targeting in tests |
interface NotifyRenderArgs {
id: string; // Notification ID
duration: number; // Duration in ms
alignment: NotifyAlignment; // Position
onClose: () => void; // Function to close this notification
data?: any; // Custom data (if provided)
timeRemaining: number; // Time remaining until auto-close (in ms)
dataTestId?: string; // Test ID (if provided)
}import { notify, NotifyAlignment } from 'react-simple-notify';
notify.open({
alignment: NotifyAlignment.topRight,
duration: 5000,
pauseOnHover: true,
render: ({ onClose }) => (
<div className="notification success">
<span>✓ Operation completed successfully!</span>
<button onClick={onClose}>✕</button>
</div>
),
});Updates an existing notification by ID.
const id = notify.open({
duration: 0,
data: { progress: 0 },
render: ({ data }) => (
<div>Loading... {data.progress}%</div>
),
});
// Update progress
notify.update(id, { data: { progress: 50 } });
// Update to completion
notify.update(id, {
duration: 3000,
render: () => <div>✓ Complete!</div>,
});Closes a specific notification by ID.
const id = notify.open({
render: () => <div>I can be closed programmatically</div>,
});
// Later...
notify.close(id);Closes all active notifications.
notify.closeAll();Available positioning options:
enum NotifyAlignment {
topLeft = 'top-left',
topRight = 'top-right',
topCenter = 'top-center',
bottomLeft = 'bottom-left',
bottomRight = 'bottom-right',
bottomCenter = 'bottom-center',
}Usage:
import { notify, NotifyAlignment } from 'react-simple-notify';
notify.open({
alignment: NotifyAlignment.topCenter,
render: () => <div>Notification at top center</div>,
});Set global configuration for all notifications.
| Parameter | Type | Default | Description |
|---|---|---|---|
alignment |
NotifyAlignment |
bottomLeft |
Default position for all notifications |
reverse |
boolean |
false |
New notifications appear at bottom of stack |
notifyComponent |
React.ComponentType |
Fragment |
Wrapper component for notification content |
animationConfig |
AnimationConfig |
Default animations | Custom enter/exit animations |
maxNotifications |
number |
0 |
Maximum notifications per position (0 = unlimited) |
pauseOnHover |
boolean |
false |
Global pause on hover setting |
import { config, NotifyAlignment } from 'react-simple-notify';
config.set({
alignment: NotifyAlignment.topRight,
maxNotifications: 3,
pauseOnHover: true,
});import { config } from 'react-simple-notify';
const NotificationWrapper = ({ children }) => (
<div className="notification-wrapper">
{children}
</div>
);
config.set({
notifyComponent: NotificationWrapper,
});Reset configuration to default values.
config.reset();Customize container spacing:
:root {
--rsn-container-padding: 16px; /* Space from screen edges */
--rsn-container-gap: 12px; /* Space between notifications */
}Style your notifications with regular CSS:
notify.open({
render: () => (
<div className="my-notification">
<span>Custom styled notification</span>
</div>
),
});.my-notification {
background: white;
padding: 16px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}Override default animations:
import { config } from 'react-simple-notify';
config.set({
animationConfig: {
enter: {
duration: 300,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
keyframes: ({ alignment }) => [
{ opacity: 0, transform: 'translateY(-20px)' },
{ opacity: 1, transform: 'translateY(0)' },
],
},
exit: {
duration: 200,
easing: 'ease-in',
keyframes: () => [
{ opacity: 1, transform: 'scale(1)' },
{ opacity: 0, transform: 'scale(0.8)' },
],
},
},
});| Property | Type | Description |
|---|---|---|
duration |
number |
Animation duration in milliseconds |
easing |
string |
CSS easing function |
keyframes |
(args) => Keyframe[] |
Function returning keyframes array |
const showProgress = () => {
let progress = 0;
const id = notify.open({
duration: 0,
data: { progress: 0 },
render: ({ data }) => (
<div className="notification">
<h4>Uploading...</h4>
<div className="progress-bar">
<div style={{ width: `${data.progress}%` }} />
</div>
<p>{data.progress}%</p>
</div>
),
});
const interval = setInterval(() => {
progress += 10;
if (progress > 100) {
clearInterval(interval);
notify.update(id, {
duration: 3000,
render: () => (
<div className="notification success">
<h4>✓ Upload Complete!</h4>
</div>
),
});
} else {
notify.update(id, { data: { progress } });
}
}, 300);
};notify.open({
duration: 0, // Never auto-close
pauseOnHover: false,
render: ({ onClose }) => (
<div className="notification warning">
<h4>⚠ Action Required</h4>
<p>Please review your settings</p>
<button onClick={onClose}>Dismiss</button>
</div>
),
});notify.open({
duration: 5000,
pauseOnHover: true,
render: ({ onClose, timeRemaining, duration }) => {
const progress = ((duration - timeRemaining) / duration) * 100;
return (
<div className="notification">
<h4>Auto-closing notification</h4>
<p>This will close in {Math.ceil(timeRemaining / 1000)}s</p>
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
</div>
<button onClick={onClose}>Close now</button>
</div>
);
},
});const saveData = async () => {
const id = notify.open({
duration: 0,
render: () => <div>Saving...</div>,
});
try {
await fetch('/api/save', { method: 'POST' });
notify.close(id);
notify.open({
render: () => <div>✓ Saved successfully!</div>,
});
} catch (error) {
notify.update(id, {
duration: 0,
render: ({ onClose }) => (
<div className="notification error">
<span>✕ Failed to save</span>
<button onClick={onClose}>Close</button>
</div>
),
});
}
};import { config } from 'react-simple-notify';
config.set({
maxNotifications: 3, // Only 3 visible at once per position
});
// Older notifications are automatically removed
for (let i = 0; i < 10; i++) {
notify.open({
render: () => <div>Notification {i + 1}</div>,
});
}
// Only the last 3 will be visibleFull TypeScript support included. All types are exported:
import type {
NotifyRenderArgs,
NotifyOptions,
NotifyAlignment,
AnimationConfig,
ConfigProps,
} from 'react-simple-notify';- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
MIT © GruFFix