Build a Notification Inbox / Activity Feed

“Build a notification inbox” is a frontend interview prompt that overlaps with comments, message threads, and feeds — but with its own twists around grouping, read state, and the bell-icon UX. Used in GitHub, Linear, Slack, and most SaaS products. The interview tests whether you can model the data, render efficiently, and handle the cross-cutting concerns of read/unread state.

Clarify scope

  • One unified inbox or separated by category (mentions, replies, system)?
  • Group similar notifications (“Alice and 5 others reacted”)?
  • Mark-as-read individually or per-thread?
  • Real-time updates while open?
  • Mobile parity?

The data model

{
  id: "n1",
  type: "comment_reply",
  actor: { id, name, avatar },
  target: { type: "issue", id, title, url },
  read: false,
  createdAt: "...",
  groupKey: "issue-42-comments",  // for grouping
  body: "...optional preview..."
}

Each notification references an actor (who did it), a target (what it is about), and a type (template for rendering). Group key lets you collapse related notifications.

Grouping

Two approaches:

  • Server-side grouping: backend returns a single “Alice and 5 others commented” with the underlying notification IDs. Cleaner, but server has to know your UX rules.
  • Client-side grouping: backend returns raw notifications; client groups by key in memory. More flexible; requires careful state management.

Most modern systems do client-side grouping for flexibility. Group by (groupKey, day) typically; collapse until expanded.

Read state — the senior signal

  • Per-notification read state, persisted server-side
  • Optimistic update on mark-as-read; reconcile on server response
  • “Mark all as read” must be efficient — single server call, batch update
  • Unread count must stay in sync across devices via the WebSocket subscription
  • Distinguish “read” from “seen” — opening the inbox marks-as-seen; clicking marks-as-read

The bell icon

  • Badge count = unread notifications
  • Update reactively to incoming notifications
  • Pulse animation on new arrival (subtle; not Slack-loud)
  • Click opens the panel; clicks outside close it
  • Keyboard: Cmd-K or N opens; Escape closes

Real-time updates

  • WebSocket subscription pushes new notifications
  • Insert at top of list with subtle animation
  • Bump unread count optimistically
  • Group with existing notifications if same group key
  • If panel is closed, just bump the bell badge

Infinite scroll

  • Cursor-based pagination — load 20 at a time
  • Sentinel div + Intersection Observer triggers next page
  • Show skeleton or “Loading…” at the bottom during fetch
  • “You’re all caught up!” footer when no more notifications

Filtering and tabs

  • “All”, “Mentions”, “Unread”, custom filters
  • Persist active tab in URL or localStorage
  • Counts per tab updated reactively

Mobile considerations

  • Bottom sheet or full screen, not a popover (popover hides keyboard awkwardly)
  • Swipe-to-dismiss or swipe-to-archive on each notification
  • Pull-to-refresh

Accessibility

  • Bell icon has aria-label="Notifications, N unread"
  • Panel uses role="dialog" with focus trap when open
  • List uses role="feed" for screen reader semantics; each item is an article
  • Live region announces new notifications: “New notification from Alice”
  • Keyboard navigation: arrow keys move between items, Enter opens, Cmd-A marks all read

Performance

  • Memoize notification components by ID
  • Virtualize for users with 1000+ notifications (rare but real)
  • Debounce read-state updates (every 500ms instead of every action)
  • Avoid re-rendering the whole list on bell-badge change

Edge cases interviewers love

  • Notification arrives while user is mid-interaction with another notification — do not steal focus
  • Notification refers to a deleted resource (comment removed) — show graceful tombstone
  • User receives 100 notifications in a minute — group aggressively, do not animate every arrival
  • Cross-device read state — opens panel on phone, reads, opens on desktop minutes later — should reflect read state

What separates senior from staff

Senior candidates handle real-time updates and read state correctly. Staff candidates discuss the grouping data model, the seen/read distinction, and cross-device sync. Principal candidates raise the privacy story (which actions warrant a notification, who controls preferences).

Frequently Asked Questions

Library options for production?

Knock and Magic Bell are commercial notification platforms with embeddable inbox UIs. For self-built, the architecture above is standard.

How do I handle email + push + in-app coherently?

Server holds the canonical notification record; channels are derived (email if not seen in 5 min, push if mobile-only). Read state syncs across all surfaces.

What about notification preferences?

Per-category (mentions, replies, follows, system) opt-out preferences. Surface in settings. Server respects when generating; client should not have to filter post-hoc.

Scroll to Top