# Build a Comments Thread: Nested Replies, Pagination, Optimistic Updates

Source: https://www.techinterview.org/post/3233475316/build-comments-thread-nested-replies-pagination-optimistic/
Updated: 2026-06-30 · techinterview.org

"Build a comments thread" is one of the most realistic frontend machine-coding problems — every product team builds one eventually. The interview probes data shape, recursion, pagination, and the optimistic-update patterns that separate junior from senior frontend engineers. This guide covers the canonical interview structure.

## Clarify scope

- Flat or nested? If nested, how deep? (Reddit allows arbitrary, Hacker News too; Disqus caps; Twitter does single reply)

- Sort order: chronological, by upvotes, by recency-of-last-reply?

- Pagination of top-level vs replies?

- Edit, delete, react, mention — all in scope?

- Real-time updates (new comments while reading)?

## Data shape

Nested comments are usually delivered as a flat list with parent IDs:


```
{
  id: "c1",
  parentId: null,
  body: "...",
  author: {...},
  createdAt: "...",
  replyCount: 5,
  cursor: "..."
}
```

The client builds a tree from the flat list. Avoid receiving nested JSON from the server — pagination and partial loading get hard.

## Pagination strategies

- **Top-level page:** cursor-based pagination of root comments

- **Reply expansion:** "Show 5 more replies" loads paged replies for one parent

- **Deep nesting:** "Continue this thread" link loads a sub-tree on a separate route (Reddit pattern)

## Building the tree on the client


```
function buildTree(comments) {
  const map = new Map(comments.map(c => [c.id, { ...c, children: [] }]));
  const roots = [];
  for (const c of map.values()) {
    if (c.parentId) map.get(c.parentId)?.children.push(c);
    else roots.push(c);
  }
  return roots;
}
```

Render each level recursively. Limit recursion depth in JSX with a "depth" prop — past a threshold, render a "Continue" link instead of nesting further.

## Optimistic updates — the senior signal

When the user posts a reply:

- Generate a temp client-side ID

- Insert the comment immediately into the local tree

- Mark it visually (slightly faded, "Posting..." indicator)

- Send the request

- On success: replace temp ID with server ID; mark as confirmed

- On failure: keep the comment but show a "Retry / Discard" affordance; do not silently delete

Junior candidates skip the failure branch. That is the test.

## Edit and delete

- Edit: optimistic update with old-content kept until server confirms; on failure, revert with a clear toast

- Delete: tombstone pattern — keep the node, replace body with "[deleted]" so children remain visible. Most platforms (Reddit, HN) do this.

## Real-time updates

If new comments can arrive while reading:

- WebSocket subscription to the thread

- Buffer arrivals; show a "5 new replies — click to load" banner instead of inserting silently

- Inserting silently is hostile — the user loses scroll position

## Performance

- Virtualize long top-level lists (react-virtual, TanStack Virtual)

- Memoize comment components by ID — avoid re-rendering the whole tree on a single edit

- Defer rendering of deeply nested comments below the fold (Intersection Observer)

- Cap initial render depth; expand on user action

## Accessibility

- Each comment as a `<article>`

- Reply form is a labeled `<form>` nested below the parent comment

- "Show N replies" is a `<button>` with `aria-expanded`

- Focus management: when a reply form opens, move focus to the textarea; when closed, return to the trigger button

## Spam and moderation hooks

- "Report" action on each comment

- Admin/mod controls behind feature flag

- "Collapsed by community" state for downvoted comments (HN/Reddit pattern)

## Edge cases interviewers love

- User edits a comment while a child reply is being typed — no data loss

- Network drops mid-post — clear retry UX

- User deletes a comment with active replies — tombstone, do not orphan children

- Reply count out of sync after pagination — recalculate on insertion

## Frequently Asked Questions

### Should I use a library?

For interview, no. For production, react-comments-section and similar exist; most teams build their own because requirements diverge.

### How do I handle 10,000 comments?

Lazy-load top-level comments via cursor pagination; never render all at once. Virtualize the visible window.

### What about Markdown rendering?

Use a battle-tested library (markdown-it, react-markdown). Sanitize aggressively. Disable raw HTML; allow only the markdown subset you intend.
