The comment thread UI ships Tuesday. On Wednesday the API starts returning reply-to-reply data and a support ticket arrives: “every reply renders at the same indent — the conversation structure is invisible.” The engineer opens the component expecting a layout bug. It isn't. The data is a flat array of comments with parentId strings, and the render is a single .map that indents once if parentId exists. It worked for depth 1 because every reply sat “one level in”. It collapses the moment someone replies to a reply.

Below is the widget exactly as it ships. Five comments: c1 is the root, c2 is a reply to c1 (depth 1), c3 is a reply to c2 (depth 2), c4 is a reply to c3 (depth 3), c5 is another reply to c1 (depth 1). The REAL depth varies 03; the render puts them all at the same indent. Tap any comment to see its real parent-chain vs. its rendered position.

Live thread · flat {id, parentId} arraytap a reply to inspect
Depth readoutTap any comment above to see its rendered indent vs. real depth.
The flat version · shipped as-is
1
// The flat version every first-pass ships.
2
// Comments stored as a flat array with parentId strings.
3
function BrokenCommentThread({ comments }) {
4
  return (
5
    <ul>
6
      {comments.map((c) => (
7
        <li key={c.id} style={{
8
          marginLeft: c.parentId ? 12 : 0,  // ← hopeful indent hack
9
        }}>
10
          <strong>{c.author}</strong> {c.body}
11
        </li>
12
      ))}
13
    </ul>
14
  );
15
}
16
17
// What breaks:
18
//   1. All replies render at the SAME indent (12px), regardless of
19
//      whether they're replies-to-root or replies-to-reply-to-reply.
20
//   2. Rendering order depends on array order — a reply-to-c2 can
21
//      render ABOVE c2 if the array isn't pre-sorted.
22
//   3. Every layout hack (parentId.length, recursive indent lookup)
23
//      requires reconstructing the tree structure on every render.
24
//   4. The tree shape on screen never matches the data.
Step 1 / 3 · Probe the thread

Before we answer anything, tap comment c3 above. Look at its real depth in the readout panel versus where it renders on screen. Then tap c4 and compare. Once you've seen the mismatch, continue to the first gate.

probe
where
indent
residue