# Bill Of Materials Tree Grid With Rolled-Up Cost And Supply Risk

> Multi-level DataGrid tree mode for a bill of materials: recursive subRows at arbitrary depth, DataGridTableRowExpand depth indentation, quantity multiplied down the tree, cost and lead time rolled up, supply risk badges, a depth level control and a grand-total footer.

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-4).

- Page, with a preview: https://shadcn.exchange/r/reui/data-grid-grouping-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/data-grid-grouping-4-light-ef9fbe8190fbf38b.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-4

## Dependencies

- npm packages: `@tanstack/react-table`, `cn`, `sonner`
- Registry items: `@reui/badge`, `@reui/data-grid`, `@reui/frame`, `@reui/icon-tile`, `button`, `@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 bill of materials cost explorer for hardware engineering and sourcing teams that need to expand a product into systems, assemblies, sub-assemblies and purchased parts, and see where cost, lead time and supply risk actually sit.

## Best Fit
- bill of materials and product structure explorers
- any hierarchy deeper than one grouping level: cost centres, chart of accounts, folder trees, org rollups, cloud spend by account and service
- costed hierarchies where a parent value must be the sum of its children
- sourcing reviews that need critical-path lead time and per-part supply status

## Main Pieces
- a `Frame` shell with title, product and revision line, three build KPIs (Build cost, Critical path, At risk), and an `Export` action
- a ReUI `DataGrid` in true tree mode: recursive `getSubRows` with no depth cap, and the `DataGridTableRowExpand` primitive supplying per-level indentation from `row.depth`
- a leaf spacer next to the expand toggle so a part and an assembly at the same depth share one label spine
- a depth control (`Collapse`, `1`, `2`, `All`) that expands the whole tree to a chosen level and reports `-1` once a row is toggled by hand
- a per-node icon map (`BOM_NODE_ICONS`) keyed by node id, falling back to `BOM_KIND_ICONS`, so a long tree does not repeat one glyph
- a supply filter whose options render the same `Badge` as the column, so the option matches the cell it selects
- rolled-up measures computed once in `buildBomRows`: effective quantity multiplied down the tree, extended cost and part count summed upward, longest lead time as the critical path, worst descendant supply status
- an extended-cost cell that pairs the rolled-up figure with a share-of-build donut and percentage on branch rows, so the expensive branch reads before any number does
- ancestor-preserving search: a matching part keeps its whole path back to the product, and a matching assembly keeps its subtree
- a `DataGridTable` grand-total footer built one cell per visible column so it tracks column toggles
- a `Display` popover with density, a part detail line, and column toggles for number, unit cost, lead time, and supply
- original cargo e-bike BOM data across four depth levels, pinned to a fixed revision date

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

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

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

## How To Use
1. Replace `BOM_TREE` with your real product structure; nodes are self-recursive, so depth is whatever your data has.
2. Keep raw per-parent `qty`, per-unit `unitCost`, and `leadTimeDays` in the data. Every rolled-up figure derives in `buildBomRows`.
3. Preserve globally unique node ids; `getRowId` returns them flat, so expansion state stays predictable at any depth.
4. `getFirstPathExpandedState` opens the first branch at every level on first paint; swap it for `getLevelExpandedState(rows, n)` if you want a uniform depth instead.
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 row actions and `Export` to your PLM, ERP, or sourcing flow.

## What Not To Do
- Do not hand-roll the expand toggle or a fixed `ps-8` indent; `DataGridTableRowExpand` is what makes depth beyond one level work.
- Do not cap `getSubRows` on a node kind; return `row.children` at every level or the tree silently flattens.
- Do not hard-code subtotals, the build cost, or the critical path; they derive from the leaf parts.
- Do not drop the leaf spacer beside the toggle, and keep it tracking the density switch; without it, parts and assemblies at the same depth lose their shared label spine.
- Do not band the root rows with a background; depth is carried by the indent, the icon and the label weight.
- Do not put a solid `secondary` badge in the `FrameHeader`; it sits on the `Frame` gutter tint and reads as plain text.
- Do not let search return bare leaves; a part without its ancestors is unreadable in a tree.
- Do not copy real manufacturer names, part numbers, or costs into this block.

## Real Data Integration
Back each node with a stable id, part number, name, kind, per-parent quantity, and, for purchased parts, a unit cost, lead time, supply status and supplier. For server-backed data, fetch the flat node list with a parent id and build the child arrays on the client or server before passing the tree into TanStack Table. Roll quantity down the tree and cost up it, so a sub-assembly used twice counts its parts twice. If filtering becomes server-backed, return whole ancestor paths rather than bare leaves, and preserve the same controlled expanded state and flat row id shape.

## LLM Guidance
Use this block when the user asks for a data grid whose hierarchy is deeper than one grouping level: a bill of materials, product structure, chart of accounts, cost rollup, folder tree, or any parent-child table where parent values must derive from descendants. This is the tree-mode sibling in the grouping family; `data-grid-grouping-1` is a flat headerless grouped list, `data-grid-grouping-2` a one-level region rollup with a grand total, and `data-grid-grouping-3` a grouped cohort heatmap. Preserve the recursive `getSubRows`, the `DataGridTableRowExpand` indentation with its leaf spacer, the single-pass `buildBomRows` derivation, the ancestor-preserving filter, the depth level control, the share-of-build donut, the per-node icon map, the derived grand-total footer, the portable `IconPlaceholder` mappings, and the semantic Badge usage unless the brief explicitly changes the hierarchy or the measures.
