ReviseAlgo Logo

Machine Coding

Nested Comments

Master building a Nested Comments thread in React, covering recursive reply trees, inline reply inputs, comment editing, deletion, and deeply nested immutable state updates.

Last Updated: July 15, 2026 14 min read

1. Learning Objectives

In this challenge, you will build a Nested Comments thread system. By the end of this guide, you will be able to:

  • Model comment data as a recursive tree with replies arrays.
  • Render recursive comment components with visual nesting indicators.
  • Add inline reply inputs that appear below a specific comment.
  • Insert replies into deeply nested positions immutably.
  • Delete and edit comments at any nesting depth.

2. Overview

Nested Comments is a top-tier machine coding challenge because it combines recursion, immutable deep state updates, and complex UI interactions (reply, edit, delete) — all within a single component tree. It is structurally similar to the File Explorer but with richer interaction patterns.

3. Why This Challenge Matters

Nested comment systems teach patterns used across the web:

  • Real-World Ubiquity: Reddit, YouTube, GitHub, and Hacker News all use nested/threaded comment systems. Understanding how they work is essential.
  • Multiple Operations on Recursive Data: Unlike the File Explorer which focuses on insert and delete, nested comments also require editing existing nodes and toggling inline reply UIs — forcing you to manage multiple interaction states per node.

4. Real-World Analogy

Think of nested comments like a family tree conversation at a dinner table:

  • Top-Level Comments: The grandparents at the head of the table start a conversation topic. These are the root-level comments.
  • Replies: A child responds to their parent's comment, and a grandchild responds to the child. Each response creates a deeper branch in the conversation tree.
  • Editing: Someone corrects what they said — they modify their own comment node without affecting any replies below it.
  • Deleting: Removing someone's comment (and all their replies below) is like that person and their conversation branch leaving the table entirely.

5. Core Concepts

Below is a comparison of flat comment lists versus nested comment trees:

Property Flat Comments Nested Comments
Data Shape [{ id, text }, ...] { id, text, replies: [{ id, text, replies: [...] }] }
Reply Relationship A flat parentId field; rendering requires grouping logic. Replies are directly nested inside the parent node's replies array.
Rendering Single .map() with conditional indentation based on depth. Recursive component: each comment renders itself for all its replies.
State Updates Simple array operations (filter, map). Recursive traversal to locate the target node, then spread at every ancestor level.

6. Syntax & API Reference

This example shows the recursive helper functions for inserting, editing, and deleting comments:

7. Visual Diagram

This diagram illustrates how the comment tree structure maps to recursive rendering:

8. Live Example — Full Working Code

Below is a complete HTML page demonstrating a nested comment thread with reply, edit, and delete:

What just happened? Each Comment component recursively renders itself for all items in its replies array. The insertReply, editComment, and deleteComment helper functions traverse the tree recursively, locating the target node and creating new objects via spreading at every level. The depth prop controls visual indentation and border color progression.

9. Interactive Playground

Try It Yourself Challenges:

  1. Add a "collapse/expand" toggle that hides or shows all replies below a specific comment.
  2. Add upvote/downvote buttons with vote counts for each comment.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Replies appear at wrong level The recursive insert function doesn't recurse into replies arrays, so it only checks top-level comments and silently fails for nested ones. comments.map(c => {
if (c.id === id) ...
return c; // No recursion
})
comments.map(c => {
if (c.id === id) ...
return { ...c,
replies: insertReply(
c.replies, id, text)
};
})
Edit overwrites all comments with same text Using AllowMultiple or text-based matching instead of ID-based matching causes all comments with the same text to be edited. if (c.text === oldText)
return { ...c, text: newText }
if (c.id === targetId)
return { ...c, text: newText }

11. Best Practices

  • Keep interaction state local: The reply input visibility, edit mode, and edit text should be local state in each Comment component, not lifted to the parent.
  • Use IDs, not text, for targeting: Always match comments by their unique id, never by text content.
  • Limit visual nesting depth: After 4-5 levels, consider flattening the thread or adding a "Continue this thread →" link, as very deep nesting becomes hard to read.
  • Color-code depth levels: Use progressive border colors or left-border indicators to help users visually track which comment is a reply to which parent.

12. Browser Compatibility/Requirements

This project uses standard JavaScript and React. No special browser APIs are required.

13. Interview Questions

Q1: What is the time complexity of inserting a reply into a nested comment tree?

Answer: O(N) where N is the total number of comments in the tree. The recursive insert function must visit every node in the worst case to find the target comment. Each node is visited once and a new object is created via spreading, so the operation is linear.

Q2: How would you optimize performance for a thread with thousands of comments?

Answer: Use a normalized flat data structure (like a hash map keyed by comment ID with a parentId field) instead of deeply nested objects. This allows O(1) lookup for any comment. For rendering, use virtualization to render only visible comments and lazy-load deeper thread branches.

14. Debugging Exercise

Identify why deleting a deeply nested reply also deletes its parent comment:

View Solution

Diagnosis: The function filters both the top level AND each comment's direct replies, but it doesn't recurse. If the target is nested 3 levels deep, it won't be found. Additionally, the non-recursive filter only checks one level of replies.

Fix: Recurse into each comment's replies:

15. Practice Exercises

Exercise 1: Timestamp and relative time

Add a createdAt timestamp to each comment and display it as relative time (e.g., "2 minutes ago", "1 hour ago"). Update the display dynamically using a timer interval.

16. Scenario-Based Challenge

The real-time collaborative comments challenge:

Multiple users are viewing and adding comments simultaneously via WebSocket. Describe how you would handle real-time updates: when user A adds a reply, user B should see it appear immediately. Address potential issues like conflicting edits, race conditions, and optimistic updates.

17. Quick Quiz

Q1: Why must recursive helper functions return new objects at every level, not just at the target node?

A) Because JavaScript doesn't support in-place mutations

B) Because React compares by reference — if parent objects aren't new, React won't detect changes in their children

C) Because the browser requires new DOM nodes

Answer: B — React uses referential equality checks. If a parent comment object is the same reference, React assumes nothing changed inside it, even if a deeply nested reply was modified. Creating new objects at every ancestor level ensures React detects the change.

18. Summary & Key Takeaways

  • Model comments as a tree with replies arrays at each node.
  • Use recursive components that render themselves for each reply.
  • Create separate recursive helper functions for insert, edit, and delete operations.
  • Keep interaction state (reply input, edit mode) local to each Comment component.
  • Match comments by unique ID, never by text content.

19. Cheat Sheet

Operation Syntax Pattern
Insert reply { ...c, replies: [...c.replies, newReply] }
Edit text if (c.id === id) return { ...c, text: newText }
Delete node comments.filter(c => c.id !== id).map(c => { ...c, replies: deleteComment(c.replies, id) })
Recurse into children return { ...c, replies: fn(c.replies, id, ...) }
---