Notifications are essential for improving user experience in modern applications. They keep users informed about events like successful actions, errors, or warnings. In this post, I'll show you how to build a reusable and customizable React Notification System from scratch.
Key Features of Our Notification System
Multiple Types: Supports success, error, warning, and info.
Auto-dismiss: Notifications disappear after a set duration.
Customizable Positions: Place notifications in any corner of the screen.
Stylish Animations: Smooth fade-in and fade-out effects.
Reusable Hook: Simple integration using a custom React hook.
By the end of this guide, you'll have a fully functional notification system that's lightweight and easy to integrate into your React projects.
Flow
Creating the Custom Hook
import { useState, useEffect } from 'react';
import Notification from '../components/Notification';
const useNotification = (position = 'bottom-left') => {
const [notifications, setNotifications] = useState([]);
const addNotification = (notification) => {
const id = Math.random().toString(36).substring(7);
setNotifications((prev) => [...prev, { ...notification, id }]);
};
const removeNotification = (id) => {
setNotifications((prev) => prev.filter((notif) => notif.id !== id));
};
useEffect(() => {
const timers = notifications.map((notification) =>
notification.duration
? setTimeout(() => removeNotification(notification.id), notification.duration)
: null
);
return () => timers.forEach((timer) => timer && clearTimeout(timer));
}, [notifications]);
const NotificationComponent = (
<div className={`notification-container ${position}`}>
{notifications.map((notification) => (
<Notification
key={notification.id}
{...notification}
onClose={() => removeNotification(notification.id)}
/>
))}
</div>
);
return { triggerNotification: addNotification, NotificationComponent };
};
export default useNotification;
State Management: The hook uses the useState hook to maintain an array of notifications, where each notification includes an id for tracking.
Adding Notifications: The addNotification function accepts a notification object, generates a unique id, and adds it to the notifications list.
Removing Notifications: The removeNotification function removes a notification from the list by its id.
Automatic Dismissal: Using useEffect, the hook automatically removes notifications after their specified duration by setting a setTimeout.
Rendering Notifications: The NotificationComponent maps through the notifications and renders a Notification component for each one, passing the necessary props and an onClose callback to dismiss them.
Return Value: The hook returns a triggerNotification function to trigger new notifications and the NotificationComponent to render the notifications on the screen.
Building the Notification Component
import { useEffect, useState } from 'react';
import { AiOutlineClose } from 'react-icons/ai';
import './Notification.css';
const Notification = ({ type = 'info', message, onClose, duration = 5000 }) => {
const [isExiting, setIsExiting] = useState(false);
const handleClose = () => {
setIsExiting(true);
setTimeout(() => onClose && onClose(), 300);
};
useEffect(() => {
const timer = setTimeout(handleClose, duration);
return () => clearTimeout(timer);
}, [duration]);
return (
<div className={`notification ${type} ${isExiting ? 'exit' : ''}`}>
<span>{message}</span>
<AiOutlineClose className="close-button" onClick={handleClose} />
</div>
);
};
export default Notification;
Fancy Styling
.notification {
display: flex;
align-items: center;
padding: 10px 15px;
border-radius: 5px;
color: white;
animation: fadeIn 0.3s ease-out;
transition: opacity 0.3s, transform 0.3s;
}
.notification.exit {
opacity: 0;
transform: translateY(20px);
}
.notification.info {
background-color: #2196f3;
}
.notification.success {
background-color: #4caf50;
}
.notification.warning {
background-color: #ff9800;
}
.notification.error {
background-color: #f44336;
}
.close-button {
margin-left: auto;
cursor: pointer;
}
.notification-container {
position: fixed;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 10px;
}
.notification-container.bottom-left {
bottom: 10px;
left: 10px;
}
.notification-container.bottom-right {
bottom: 10px;
right: 10px;
}
.notification-container.top-left {
top: 10px;
left: 10px;
}
.notification-container.top-right {
top: 10px;
right: 10px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
javascript
Using the Notification System
Now, integrate the hook into your app.
import useNotification from './hooks/useNotification';
function App() {
const { NotificationComponent, triggerNotification } = useNotification('top-right');
return (
<div>
<button
onClick={() =>
triggerNotification({
type: 'success',
message: 'This is a success message!',
duration: 3000,
})
}
>
Trigger Success
</button>
<button
onClick={() =>
triggerNotification({
type: 'error',
message: 'This is an error message!',
duration: 3000,
})
}
>
Trigger Error
</button>
{NotificationComponent}
</div>
);
}
export default App;
useNotification
vs. useContext
While the custom useNotification hook is a great solution for managing notifications, React’s useContext hook is a common alternative for global state management. Let’s compare the two approaches.
1. Use Case
useNotification
: Best for specific, isolated functionality like notifications.useContext
: Ideal for sharing global state or logic across the entire application.
2. Simplicity
useNotification
: Simple and lightweight, requiring no additional setup.useContext
: Requires setting up a context provider and consumers, which can be verbose for isolated features.
3. Flexibility
useNotification
: Limited to notifications; not a global state manager.useContext
: Flexible for managing any kind of shared state, including notifications.
4. Performance
useNotification
: Localized state ensures updates only affect components related to notifications.useContext
: Context updates can trigger unnecessary re-renders across all consuming components unless carefully optimized.
The choice between useNotification
and useContext
depends on your application’s needs. If you’re building a feature-specific system like notifications, useNotification
provides a lightweight and elegant solution. However, for managing broader state across multiple components, useContext
is a better choice.
Happy Coding!
Top comments (0)