In-App Notification Center
Bell icon, unread count, real-time updates, and mark-as-read
What You’ll Build
After following this guide, you will have a working implementation of in-app notification center in your project. Build an in-app notification center (the bell icon with unread count). Notifications appear in real-time using SSE or WebSockets, persist in the database, support mark-as-read, and show a dropdown panel with notification history.
Use Cases & Problems Solved
- Send transactional emails reliably without landing in spam folders
- Set up notification systems that scale with your user base
- Avoid building email delivery infrastructure from scratch
Prerequisites
- React 18+
- Backend API
- Database
Step-by-Step Implementation
Notification API and real-time delivery
The following snippet shows how to notification api and real-time delivery. Copy this into your project and adjust the values for your environment.
// Server: create and broadcast notifications
async function createNotification(userId: string, title: string, body: string, link?: string) {
const notification = await db.notification.create({
data: { userId, title, body, link, read: false },
});
// Push to connected client via SSE
sseClients.get(userId)?.write(\`data: \${JSON.stringify(notification)}\
\
\`);
return notification;
}
// API endpoints
app.get('/api/notifications', auth, async (req, res) => {
const notifications = await db.notification.findMany({
where: { userId: req.user.id },
orderBy: { createdAt: 'desc' },
take: 50,
});
const unreadCount = await db.notification.count({
where: { userId: req.user.id, read: false },
});
res.json({ notifications, unreadCount });
});
app.patch('/api/notifications/:id/read', auth, async (req, res) => {
await db.notification.update({ where: { id: req.params.id }, data: { read: true } });
res.json({ ok: true });
});
app.post('/api/notifications/read-all', auth, async (req, res) => {
await db.notification.updateMany({ where: { userId: req.user.id, read: false }, data: { read: true } });
res.json({ ok: true });
});
⚠️ Don’t Do This
❌ Polling for new notifications every second
setInterval(async () => {
const data = await fetch('/api/notifications').then(r => r.json());
setNotifications(data);
}, 1000); // 86,400 requests per user per day!
✅ Use SSE or WebSocket for real-time push
const eventSource = new EventSource('/api/notifications/stream');
eventSource.onmessage = (e) => {
const notification = JSON.parse(e.data);
addNotification(notification);
};
// Zero polling — server pushes only when new notifications exist
Testing
Verify your implementation with these tests:
// __tests__/in-app-notification-center.test.ts
import { describe, it, expect } from 'vitest';
describe('In-App Notification Center', () => {
it('should initialize without errors', () => {
// Test that the setup completes successfully
expect(() => setup()).not.toThrow();
});
it('should handle the primary use case', async () => {
const result = await execute();
expect(result).toBeDefined();
expect(result.success).toBe(true);
});
it('should handle edge cases', async () => {
// Test with empty/null input
const result = await execute(null);
expect(result.error).toBeDefined();
});
});
Verification
# Open two browser tabs logged in as the same user
# Trigger a notification from the server/admin panel
# Bell icon should update with unread count in real-time
# Click notification — should mark as read and navigate to link Related Specs
Beginner
Send Emails with Resend
React Email templates, attachments, batch sending, and delivery tracking
Email & Notifications
Email & Notifications
Beginner
Toast Notification System
Custom toast component, queue management, animations, and accessibility
Frontend Patterns