TrueSpec

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
Beginner

SMS Notifications with Twilio

Send/receive SMS, verification codes, and webhook handling

Email & Notifications
Beginner

Toast Notification System

Custom toast component, queue management, animations, and accessibility

Frontend Patterns