# Build a Notification Inbox / Activity Feed

Source: https://www.techinterview.org/post/3233475334/build-notification-inbox-activity-feed/
Updated: 2026-05-06 · techinterview.org

"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.
