TreeTable
Hierarchical table with a pinned name column, horizontally scrolling data columns, search, expand / collapse all and a "Move to…" sheet — API-aligned with the web TreeTable.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-tree-tablepnpm add @shopify/flash-list react-native-reanimated tailwind-variantsUsage
import { useState } from 'react';
import { StatusChip } from '@/components/docyrus-native/status-chip';
import { TreeTable, type TreeTableColumn } from '@/components/docyrus-native/tree-table';
import { type TreeViewItem } from '@/components/docyrus-native/tree-view';
const columns: TreeTableColumn[] = [
{ id: 'status', header: 'Status', width: 130, cell: item => <StatusChip label={String(item.status)} size="sm" /> },
{ id: 'owner', header: 'Owner', accessorKey: 'owner', width: 160 },
{ id: 'progress', header: 'Progress', accessorKey: 'progress', width: 100, align: 'right' }
];
export function ProjectTree({ initial }: { initial: TreeViewItem[] }) {
const [data, setData] = useState(initial);
return (
<TreeTable
data={data}
columns={columns}
nameColumnHeader="Work item"
draggable
canDropInside={item => item.type !== 'task'}
maxHeight={420}
onDataChange={nextData => setData(nextData)} />
);
}Layout and interaction
- Rows are the flattened visible tree rendered by a
FlashListwith fixed row heights. The name column plus all data columns sit inside one horizontalScrollView; withstickyNameColumn(default) the name cells are translated by the horizontal scroll offset (reanimated), so the hierarchy stays visible while the data columns scroll underneath. - Tapping the chevron expands / collapses a folder. Tapping a row calls
onRowPress; withoutonRowPressa folder row toggles itself. - The options button next to the search opens Expand All / Collapse All (hidden with
showExpandAll={false}). Searching filters by name and opens matching branches. draggablereplaces web drag-and-drop with a "Move to…" sheet (long-press drag inside nested, horizontally scrolling rows is unreliable on a phone). Each row gets a…button (long-press works too); the sheet lists every valid target (the moved item and its subtree are excluded) plus Top level, and offers Before / Inside / After — Inside only whencanDropInside(target)allows. The move goes throughmoveTreeViewItemand firesonDataChange(nextData, details)exactly like web. Moving is disabled while a search query is active, and items withdraggable: falseget no…button.
API Reference
TreeTableProps
| Prop | Type | Default | Description |
|---|---|---|---|
data | TreeViewItem[] | - | Tree rows. The table keeps an internal copy (so moves apply immediately) that is re-seeded whenever data changes |
columns | TreeTableColumn[] | - | Data columns rendered after the name column |
variant | 'default' | 'outline' | 'ghost' | 'default' | Container style (treeViewVariants) |
size | 'sm' | 'default' | 'lg' | 'default' | Row height (36 / 44 / 52) and text size |
nameColumnHeader | ReactNode | t('ui.treeTable.name', 'Name') | Header of the name column |
nameColumnWidth | number | 200 | Name column width in dp (native addition) |
getIcon | (item: TreeViewItem, depth: number) => ReactNode | - | Icon rendered before the name |
showExpandAll | boolean | true | Show the options button with Expand All / Collapse All |
searchPlaceholder | string | t('ui.treeView.searchPlaceholder', 'Search...') | Search placeholder |
emptyText | string | t('ui.treeView.noItemsFound', 'No items found') | Text when no rows match |
draggable | boolean | false | Adds the row … button / long-press that opens the "Move to…" sheet |
canDropInside | (item: TreeViewItem) => boolean | canTreeViewItemAcceptChildren | Whether an item accepts children (the Inside position) |
onDataChange | (nextData: TreeViewItem[], details: TreeViewMoveDetails) => void | - | Tree changed by a move |
maxHeight | number | string | - | Max height (dp or 'Npx') before rows scroll vertically; without it every row renders at full height |
onRowPress | (item: TreeViewItem) => void | - | Row tapped (native addition). Without it, a folder row tap toggles the folder |
stickyNameColumn | boolean | true | Keep the name column pinned while the data columns scroll horizontally (native addition) |
className | string | - | Additional classes for the root container |
testID | string | - | Test id for the root container |
TreeTableColumn
| Field | Type | Default | Description |
|---|---|---|---|
id | string | - | Column id |
header | ReactNode | - | Header content (strings are wrapped in Text) |
accessorKey | string | - | Key read from the item when cell is not set |
cell | (item: TreeViewItem) => ReactNode | - | Custom cell renderer |
width | number | 120 | Column width in dp (web also accepts CSS strings) |
align | 'left' | 'center' | 'right' | 'left' | Cell alignment |
className | string | - | Extra classes for body cells |
headerClassName | string | - | Extra classes for the header cell |
Differences from web
refand div HTML attributes are dropped (className+testIDremain).- Drag-and-drop (dnd-kit) is replaced by the "Move to…" sheet;
onDataChange/TreeViewMoveDetailsare identical andpositionis one ofbefore/after/inside/root. TreeTableColumn.widthis a number.nameColumnWidth,onRowPressandstickyNameColumnare native additions.
Translation keys
Copy resolves through useUiTranslation(). Shared with web: ui.treeView.searchPlaceholder, ui.treeView.noItemsFound. Native additions: ui.treeTable.name, ui.treeTable.options, ui.treeView.expandAll, ui.treeView.collapseAll, ui.treeView.expand, ui.treeView.collapse, ui.treeView.moveTo, ui.treeView.moveToRoot, ui.treeView.moveBefore, ui.treeView.moveInside, ui.treeView.moveAfter.
Components
| Component | Description |
|---|---|
TreeTable | Main component |
Type Exports
| Type | Description |
|---|---|
TreeTableProps | Props for TreeTable |
TreeTableColumn | Column definition |
TreeSelect
Trigger field that opens a searchable, flattened tree in a bottom sheet — single or multi select, leaf-only selection and breadcrumb display, API-aligned with the web TreeSelect.
TreeView
Hierarchical tree with search, selection, checkboxes, long-press actions and drag-and-drop reordering — API-aligned with the web TreeView.