Purpose
Display drill-down hierarchies with tabular information (e.g., financial rollups).
Loading ...
treegridCombines hierarchical structure (tree) with columns (grid). Users can expand/collapse rows while retaining columnar data and keyboard navigation.
Hierarchy plus tabular data with expand/collapse controls and trend indicators.
Display drill-down hierarchies with tabular information (e.g., financial rollups).
Rows may own children inside role="rowgroup". Cells behave like gridcells.
Announces both coordinates and hierarchy level. Users expect tree + grid commands.
Parent rows sum child rows with expand/collapse.
Show initiatives with nested tasks plus metrics columns.
Reveal nested permissions plus per-role columns.
role="row" plus aria-level/setsize/posinset describes the hierarchy.
Rows that own children toggle aria-expanded and wrap kids in role="rowgroup".
role="gridcell" for each data cell. Use aria-describedby to connect headers.
| Action | Keys | Result |
|---|---|---|
| Move horizontally | ArrowLeft / ArrowRight | Moves between gridcells in the same row. |
| Move vertically | ArrowUp / ArrowDown | Moves between rows, staying in the same column. |
| Expand/collapse | ArrowRight / ArrowLeft on first column | Expands or collapses the focused row if children exist. |
| Jump to edges | Home / End | Moves to first/last cell of the current row. |
| Jump levels | Ctrl + Home / Ctrl + End | Moves to the first/last visible row. |
aria-levelOptionalDepth of the row within the hierarchy.
aria-expandedOptionalApplied to rows that have children to indicate whether they are visible.
aria-setsizeOptionalNumber of sibling rows at the same hierarchy level.
aria-posinsetOptionalPosition of the row inside its current set.
aria-rowcount / aria-colcountOptionalCommunicate total rows/columns especially when virtualizing.
aria-activedescendantOptionalOptional alternative to moving DOM focus for virtualized grids.
Clearly identify which column contains the expand/collapse toggles and keep focus on that cell when toggling.
When rows collapse, keep descendants in the DOM but hidden so aria relationships remain valid.
Announce summary values or totals with aria-live if expanding recalculates metrics.
Provide keyboard shortcuts or buttons to expand/collapse all nodes for faster navigation.
Manual example showing aria-level on rows.
<div role="treegrid" aria-rowcount="4" aria-colcount="3">
<div role="row" aria-level="1" aria-expanded="true">
<div role="gridcell">Marketing</div>
<div role="gridcell">$250,000</div>
<div role="gridcell">32%</div>
</div>
<div role="rowgroup">
<div role="row" aria-level="2" aria-posinset="1" aria-setsize="2">
<div role="gridcell">Brand</div>
<div role="gridcell">$140,000</div>
<div role="gridcell">18%</div>
</div>
<div role="row" aria-level="2" aria-posinset="2" aria-setsize="2">
<div role="gridcell">Product</div>
<div role="gridcell">$110,000</div>
<div role="gridcell">14%</div>
</div>
</div>
</div>Row component toggles aria-expanded and conditionally renders children.
function TreeRow({ node, level = 1 }) {
const [open, setOpen] = useState(false)
const hasChildren = Boolean(node.children?.length)
return (
<>
<div
role="row"
aria-level={level}
aria-expanded={hasChildren ? open : undefined}
>
<div role="gridcell">
{hasChildren && (
<button
aria-label={open ? 'Collapse row' : 'Expand row'}
onClick={() => setOpen((prev) => !prev)}
>
{open ? '▾' : '▸'}
</button>
)}
{node.label}
</div>
<div role="gridcell">{node.budget}</div>
<div role="gridcell">{node.share}</div>
</div>
{hasChildren && open && (
<div role="rowgroup">
{node.children.map((child) => (
<TreeRow key={child.id} node={child} level={level + 1} />
))}
</div>
)}
</>
)
}Keep expanders in the same column to reduce cognitive load.
Show summary rows for collapsed parents so users know totals without expanding.
Lazy-load children and indicate loading with aria-busy.
Forgetting to increment aria-level causes screen readers to report wrong hierarchy.
Calculate level relative to parent and keep values up to date.
Destroying child rows resets focus and aria relationships.
Keep rowgroup elements in DOM with hidden attribute.
Base role providing cell semantics.
Learn moreHierarchy without columns.
Learn moreContainer for related rows.
Learn more