Tree view (parent-child hierarchy)
Source: atrium/frontend/src/components/TreeView.jsx Category: View
Tree view — a nested display of tasks that have a parent_task relationship. Shows the decomposition of a feature into subtasks without losing the ability to see status at each level.
What it is
Section titled “What it is”A recursive indented list. Each task row shows its id, title, and a status-colored accent strip on the left. Rows with children have a disclosure chevron; clicking expands or collapses the subtree. Task data is flat in the backend (one file per task); the tree is derived client-side from the parent_task field.
Why it exists
Section titled “Why it exists”Kanban views a flat board. Some work doesn’t want to be flat — breaking down a feature into components and subtasks needs parent/child structure. The parent_task field in task YAML supports the relationship; this view makes it visible.
Code sketch
Section titled “Code sketch”function buildTree(tasks) { const byParent = {}; for (const t of tasks) (byParent[t.parent_task ?? '_root'] ??= []).push(t); return byParent;}
export default function TreeView({ tasks }) { const byParent = buildTree(tasks); const [expanded, setExpanded] = useState({});
function render(parent, depth) { return (byParent[parent] ?? []).flatMap(t => [ <Row key={t.id} task={t} depth={depth} expanded={expanded} onToggle={toggle} />, ...(isOpen(t.id) ? render(t.id, depth + 1) : []), ]); }
return <div>{render('_root', 0)}</div>;}How it’s used
Section titled “How it’s used”- Atrium — third view option alongside Board and List. Useful for features with multiple sub-components or for daily standup framing (“where is each strand of this feature”).
- Pattern generalizes to any
id+parent_idhierarchy — epics/stories, folders, org charts
Gotchas
Section titled “Gotchas”- Cycles break the renderer. If
A.parent = BandB.parent = A, the recursion doesn’t terminate. Detect or reject cycles at the backend — it should not be possible to save a task whose ancestors include itself. - Orphans need a home. If a task’s
parent_taskpoints to a deleted task, the orphan disappears from the tree because its parent is missing from the grouping. Either reparent to_rooton parent-delete, or show orphans in a special group at the top. - Deep trees are hard to scan. Limit depth or collapse deeply-nested branches by default. Atrium collapses to depth 2 on first render; user can expand further.
- Keys must be stable. React reconciliation gets confused if you use index-in-list as the key (expanding one subtree shifts indices for siblings). Always key by task id.
- Drag-to-reparent is hard. The DnD library has to know “this drop target is a tree row”, emit the new parent id, and the backend has to validate no cycles. Atrium currently has reparent via a modal, not drag.
- Performance on large trees. O(n) per render is fine up to ~500 tasks; bigger than that and virtualization helps, though virtualized trees are harder to implement than virtualized lists.
- Status rollup is a choice. Some UIs show a parent’s status as “derived from children” (“in progress” if any child is in progress). Atrium uses the parent’s own status — less magic, more predictable.
See also
Section titled “See also”- components/list-view · components/board — the flat views
- patterns/markdown-as-database — how
parent_taskis stored