# Reporting Hierarchy Tree Grid With Span Of Control And Cascading Selection

> Multi-level DataGrid tree mode for an HR org chart: recursive subRows at arbitrary depth, DataGridTableRowExpand indentation, org size and open roles rolled up each reporting line, tree-cascading row selection with a corrected tri-state checkbox, sticky header, depth control and a bulk selection bar.

A paid shadcn/ui block sold by the [@reui](https://reui.io/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/data-grid-grouping-5).

- Page, with a preview: https://shadcn.exchange/r/reui/data-grid-grouping-5
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/data-grid-grouping-5-light-3ae714ddbab75074.png
- Listed: 2026-10-13

## Buy and install

@reui sells this block, and its code is not on shadcn.exchange. Buy it from the registry, which installs it for those who have: https://shadcn.exchange/out/reui/data-grid-grouping-5

## Dependencies

- npm packages: `@tanstack/react-table`, `cn`, `sonner`
- Registry items: `@reui/badge`, `@reui/data-grid`, `@reui/frame`, `avatar`, `button`, `checkbox`, `@reui/data-grid-column-header`, `@reui/data-grid-scroll-area`, `@reui/data-grid-table`, `dropdown-menu`, `field`, `input-group`, `popover`, `select`, `switch`, `toggle-group`

## Notes from the registry

## Purpose
A reporting-hierarchy explorer for people teams and leaders who need to walk an org from the chief executive down to an individual contributor, and see span of control, open roles and who needs attention roll up every reporting line.

## Best Fit
- org charts and reporting-line browsers
- headcount planning and span-of-control review
- any people hierarchy where a manager's row must summarise their whole line
- bulk people operations that act on a team rather than a person

## Main Pieces
- a `Frame` shell with company and snapshot line, four org KPIs (Headcount, Layers, Avg span, Open roles), and an `Export` action
- a ReUI `DataGrid` in tree mode: recursive `getSubRows` with no depth cap and `DataGridTableRowExpand` supplying per-level indentation from `row.depth`
- tree-cascading row selection: checking a manager selects their entire reporting line, including collapsed reports
- a block-level `TreeRowSelect` that fixes the primitive checkbox, which is not tri-state on a tree in either direction
- a selection bar that appears only while a line is selected, with team-level verbs
- a sticky header over a bounded scroll area, so the column names survive a fully expanded org
- root-only default expansion: the leadership line is on screen and every branch below it is a deliberate click
- a depth control (`1`, `2`, `3`, `All`) that expands the whole tree to a chosen level
- rolled-up measures computed once in `buildOrgRows`: org size, direct reports, open roles, layers below, and people needing attention
- a span-of-control cell that reads direct reports large and the whole line small, and a dash for individual contributors
- ancestor-preserving search where a manager is tested on their own merits, because in an org tree the branch is a person
- a `DataGridTable` footer built one cell per visible column so it tracks column toggles
- original 18-person org across five levels, with portraits and a fixed snapshot date

## shadcn Primitives Used
- Button
- Checkbox
- DropdownMenu
- Popover
- Field
- InputGroup
- Select
- Switch
- ToggleGroup
- Avatar

## ReUI Primitives Used
- Frame
- DataGrid
- DataGridContainer
- DataGridScrollArea
- DataGridTable
- DataGridTableRowExpand
- DataGridTableRowSelectAll
- Badge

## Third-Party Libraries
- `@tanstack/react-table` for recursive nested rows, controlled expansion and selection state, stable row ids, column visibility, and flattened row rendering
- `sonner` for preview-safe action feedback

## How To Use
1. Replace `ORG_TREE` with your real reporting lines; nodes are self-recursive, so depth is whatever your org has.
2. Keep raw per-person values in the data. Every rolled-up figure derives in `buildOrgRows`.
3. Preserve globally unique person ids; `getRowId` returns them flat, so expansion and selection stay predictable at any depth.
4. `getRootExpandedState` opens the root and nothing else; swap it for `getLevelExpandedState(rows, n)` for a uniform depth.
5. Tune `TREE_INDENT` in `columns.tsx` and the leaf spacer widths together; they are a pair, and the spacer tracks the density switch because the primitive's toggle does.
6. Wire the row actions, the selection bar and `Export` to your HRIS.

## What Not To Do
- Do not ship the primitive `DataGridTableRowSelect` on a tree; it renders a manager solid-checked after one report is unchecked, and blank when every report was checked individually. Derive from `getIsAllSubRowsSelected()`.
- Do not count a selection with `getSelectedRowModel().rows`; that model is nested and drops a selected report whose manager is unselected. Use `flatRows`.
- Do not set `headerSticky` without bounding the scroll area; the viewport grows to content, so the header never sticks.
- Do not declare `meta.expandedContent` anywhere in a tree block; it shares `row.getIsExpanded()` with the tree, so every expanded manager grows a detail row inside their own subtree, and hiding the column does not switch it off.
- Do not call `table.toggleAllRowsExpanded()`; it writes the boolean `true`, which makes every row including leaves report as expanded.
- Do not cap `getSubRows` on a node kind; return `row.children` at every level or the tree silently flattens.
- Do not copy real employee names, photos, or reporting lines into this block.

## Real Data Integration
Back each person with a stable id, name, title, level, department, location, employment status, hire date, and the open roles attached directly to them. For server-backed data, fetch the flat person list with a manager id and build the child arrays on the client or server before passing the tree into TanStack Table. Sum counts upward and take the deepest branch for layers, so a manager's row always describes their whole line. If filtering becomes server-backed, return whole manager paths rather than bare people, and preserve the same controlled expanded and selection state shape.

## LLM Guidance
Use this block when the user asks for an org chart, a reporting hierarchy, a people tree, or any parent-child table of humans where a manager summarises their reports. This is the people tree in the grouping family; `data-grid-grouping-1` is a flat headerless grouped list, `data-grid-grouping-2` a one-level region rollup, `data-grid-grouping-3` a grouped cohort heatmap, and `data-grid-grouping-4` a bill-of-materials tree keyed on parts rather than people. Preserve the recursive `getSubRows`, the `DataGridTableRowExpand` indentation with its density-aware leaf spacer, the block-level `TreeRowSelect`, the flat-row selection count, the bounded scroll area under the sticky header, the single-pass `buildOrgRows` derivation, the ancestor-preserving filter, the root-only default expansion, and the semantic Badge usage unless the brief explicitly changes the hierarchy or the measures.
