Components · Data

Tree

A list that admits it has depth — and pays for it, so it has to be the clinician's own mental model.

Stable · v1.0 shadcn/ui WCAG 2.2 AA Merged: List · Tree

Overview

A tree earns its complexity only when the hierarchy is one the clinician already thinks in. Otherwise it costs two presses to reach every leaf and hides the size of the set behind a triangle. It is not a variant of a List: it carries its own expansion state, its own traversal, and one rule a flat list never needs — that a closed branch has to say what is inside it.

Tree

A tree is a list that admits it has depth. It earns its complexity only when the hierarchy is the clinician's own mental model; otherwise it costs two clicks to reach every leaf and hides the set size behind a triangle.

Containment the clinician already thinks in
  • Encounter · 13 Aug 2026
    • ECG  2
      • 15:41:02 · ST elevation V2–V4
      • 14:26:38 · no acute change

The collapsed Laboratory branch still reports that something inside it is critical. Collapse is allowed to cost you a click; it is not allowed to cost you a finding.

What a closed branch owes the reader

The rule

Collapsing may hide detail. It may never hide state. A branch that is shut still declares what is inside it that would change a decision — the count, and the highest priority among them.

What the component enforces, and what it only asks for

One rule is held here and it is the one the component exists for; the rest is the shape of the data you pass.

RuleHeld by How
A closed branch declares what is inside itThe component summary is on the type, so a branch labelled only “Studies” is not expressible.
Asserted: Tree: a closed branch declares what is inside it
Closed is hidden from view, not from searchThe component hidden="until-found", opening on beforematch. It was plain hidden until an interaction test was written for it.
Asserted: Tree: a closed branch is hidden from view, not from search
Expansion state is announcedThe component aria-expanded on each branch, with the whole label row as the target.
Asserted: AX tree: the tree item reports expanded, and its group appears when it is
One tab stop, and arrows traverse what is openThe component The items carry a roving tabindex; up, down, Home and End move between the items whose ancestors are all open, so a closed branch’s children are skipped. Driven in a browser.
Asserted: Tree: one tab stop, and arrows traverse only what is visible
Right opens and enters; left closes and leavesThe component Right opens a closed branch and steps into an open one; left closes an open branch and otherwise steps out to the parent; Enter and Space toggle. The tree held role="tree" for a long time with a button in every item and none of this behind it.
Asserted: Tree: right opens then enters, left closes then leaves
The summary is trueYou “2 recordings, 1 critical” is a string. Nothing counts the children to check it, and a summary that under-reports is worse than none.
Depth stays within two levelsYou Nesting is whatever you render.
Expansion state does not survive a user switchYou The component holds it in local state and knows nothing about sessions.
Why this is written down

A rule stated on a component page reads as a rule the component keeps. 3 of these 6 are; 3 are obligations this page was quietly handing to the reader. The summary is the whole safety argument for collapsing anything, and the component requires one without being able to check it.

Do's and don'ts

Do
  • Laboratory  6  1 critical

Collapsed, but the branch still declares what is inside it that would change a decision.

Don't
  • Laboratory

A critical result one click away and no indication it is there. Collapse has hidden state, not depth.

Do

▸ ECG   2 recordings, 1 critical

Closed, and still declaring what is inside. The reader knows there is something critical here without opening it.

Don't

▸ ECG

A closed branch with nothing said. A critical result is one press away and nothing on screen suggests it.

Do

▾ Encounter · 13 Aug

  ▸ ECG   2 recordings

  ▸ Bloods   4 results

Two levels. The reader can say what they are looking at from the screen.

Don't

▾ Patient

  ▾ Encounters

    ▾ 13 Aug

      ▾ Investigations

        ▸ ECG

Five levels deep. “Where am I” is no longer answerable from the screen, and every leaf is five presses away.

Accessibility

Outcomes of use

Clinical safety notes

Risk controls this page specifies

Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.

Implementation

$ npx shadcn@latest add https://md.notjustany.tech/r/tree.json
<Tree label="Encounter · 13 Aug 2026">
  <TreeItem label="ECG" summary="2 recordings, 1 critical">
    // the summary is required: a closed branch has to say what is in it
  </TreeItem>
</Tree>
NotJustAnyMed.Tech Design System · List & tree · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data and reference ranges shown are fabricated and illustrative.