Anatomy of a Notification Bell
Every notification bell you have used — in Gmail, Slack, GitHub, Linear — shares the same core components. Understanding these pieces helps you build (or evaluate) the right solution.
1. The Bell Icon
A clickable icon, typically in the app header or top navigation. It serves as the entry point to the notification system. The icon itself is straightforward — an SVG bell from any icon library works.
2. The Unread Badge
A small circle (usually red or blue) overlaid on the bell icon, showing the count of unread notifications. This is the attention-grabbing mechanism. It must update in real time as new notifications arrive and as the user reads them.
3. The Dropdown or Drawer
Clicking the bell opens a panel showing a list of notifications. Each item typically has a title, body text, timestamp, and read/unread state. The panel needs scroll behavior, empty states, and a "mark all as read" action.
4. Individual Notification Items
Each notification in the list is a clickable card. It may include an icon or avatar, the notification content, a relative timestamp ("2 minutes ago"), and visual distinction between read and unread states.
It looks simple on the surface. But as we will see, the real complexity lives behind the UI.
Building a Notification Bell From Scratch
Let us walk through what it actually takes to build a notification bell from scratch. Even a minimal implementation touches more systems than you might expect.
Backend Requirements
You need a persistent storage layer for notifications. At minimum, this means:
-- Minimal notifications table
CREATE TABLE notifications (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
title TEXT NOT NULL,
body TEXT,
url TEXT,
is_read BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW()
); Then you need API endpoints: list notifications (paginated), mark one as read, mark all as read, get unread count, and create notification. That is at least five endpoints to build, test, and maintain.
Real-Time Delivery
A notification bell without real-time updates feels broken. Users expect the badge count to update instantly when a new notification arrives. This means you need WebSockets or Server-Sent Events (SSE):
// Server: broadcasting to a user's WebSocket channel
wss.on('connection', (ws, req) => {
const userId = authenticate(req);
connections.set(userId, ws);
ws.on('close', () => {
connections.delete(userId);
});
});
// When creating a notification, push to connected user
function sendNotification(userId, notification) {
const ws = connections.get(userId);
if (ws) ws.send(JSON.stringify(notification));
} This is a simplified example. In production, you also need: reconnection logic when the connection drops, authentication for WebSocket connections, handling multiple browser tabs, horizontal scaling (if you have multiple server instances, users may connect to different ones), and heartbeat/keepalive mechanisms.
Frontend State Management
On the frontend, you need to manage: the list of notifications (with pagination and infinite scroll), the unread count (synced with the backend and updated via WebSocket), optimistic updates for mark-as-read actions, and proper loading and error states.
In a React app, this typically means a context provider or state management library dedicated to notifications, a custom hook for WebSocket connection management, and a set of UI components for the bell, badge, dropdown, and notification items.
The Hidden Complexity
Beyond the basics, there are edge cases that eat engineering time:
- ● What happens when a notification arrives while the dropdown is open? Does it appear at the top or wait?
- ● How do you handle notification retention? Delete after 30 days? Archive?
- ● How do you batch rapid-fire events? (e.g., 10 comments in 1 minute becoming "10 new comments")
- ● How do you handle the badge count when the user has 100+ unread notifications? Show "99+"?
- ● Accessibility: keyboard navigation, screen reader announcements for new notifications, ARIA attributes.
Altogether, a production-quality notification bell is a 2–4 week project for a full-stack developer. That is time not spent on your core product.
The Faster Path: Using Notilayer's Widget
Notilayer gives you a production-ready notification bell — icon, unread badge, dropdown inbox, and real-time delivery — with two lines of code. No backend to build, no WebSocket server to manage, no UI components to design.
Step 1: Add the Script
<script src="https://api.notilayer.com/widget/widget.js" defer></script> Step 2: Initialize
window.Notilayer.init({
appId: 'YOUR_APP_ID',
userId: 'user_123',
}); That is it. The widget renders a notification bell in your app with:
- ✓ A bell icon with real-time unread badge count
- ✓ A dropdown inbox with notification list, timestamps, and read states
- ✓ Mark as read (individual and all) functionality
- ✓ Click-through navigation to notification URLs
- ✓ Empty state for when there are no notifications
- ✓ Accessible keyboard navigation and ARIA labels
For a deeper dive on integrating with React specifically, see our React integration guide. For other frameworks, the JavaScript SDK documentation covers vanilla JS, Vue, Svelte, and more.
Customizing the Notification Bell
A notification bell that clashes with your UI is worse than no notification bell at all. Notilayer provides several customization options to ensure the widget feels native to your app.
Position
Choose where the bell appears: top-right, top-left, bottom-right, or bottom-left. Or mount it into a specific DOM element for full control:
window.Notilayer.init({
appId: 'YOUR_APP_ID',
userId: 'user_123',
selector: '#notification-bell-container',
}); Theming
Match your brand colors, border radius, and font through the theme configuration:
window.Notilayer.init({
appId: 'YOUR_APP_ID',
userId: 'user_123',
theme: {
primaryColor: '#6366f1', // Indigo accent
badgeColor: '#ef4444', // Red badge
borderRadius: '8px', // Rounded corners
fontFamily: 'Inter, sans-serif',
},
}); Learn more about widget customization options in the notification bell widget documentation.
When to Build vs When to Use a Widget
Building from scratch makes sense in a few specific scenarios:
Build if...
- ✓ Notifications are your core product feature (e.g., you are building a Slack competitor)
- ✓ You have strict data residency requirements that prevent using third-party services
- ✓ You need deeply custom notification UX that goes beyond standard bell/inbox patterns
Use a widget if...
- ✓ Notifications support your product but are not the product itself
- ✓ You want to ship in days, not weeks
- ✓ Your team's time is better spent on core features
- ✓ You want real-time delivery without managing WebSocket infrastructure
For most SaaS products, notifications are a supporting feature — important, but not the thing users are paying for. In that case, reaching for a ready-made widget is the pragmatic choice.
Key Takeaway
A notification bell looks simple but requires backend APIs, real-time delivery (WebSockets), frontend state management, and careful UX design. Building from scratch is a 2–4 week investment. Notilayer's widget gives you a production-ready notification bell with two lines of code, so you can focus on your actual product. The free plan covers most early-stage SaaS apps.