React Treebeard: Data-Driven Tree View
React Treebeard is a highly customizable and performant React component for rendering tree structures. It is data-driven, allowing developers to define the tree's structure and state through a simple data object. The current stable version is 3.2.4, released in 2019, with a beta version (4.2.4-beta.0) indicating potential future development, though the stable release cadence has been slow. Key differentiators include its robust animation capabilities, extensive styling options through decorators (leveraging `@emotion/styled`), and its programmatic approach to managing tree state, making it suitable for displaying complex hierarchical data with interactive features like toggling nodes and active states.
Common errors
-
Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=...
cause React version incompatibility with `react-treebeard`'s peer dependency requirements.fixEnsure your project's `react` and `react-dom` versions meet the `react-treebeard` peer dependency, currently `>=16.7.0` for `v3.2.0+`. -
Module not found: Error: Can't resolve '@emotion/styled' in 'path/to/node_modules/react-treebeard'
cause Missing `@emotion/styled` peer dependency.fixInstall `@emotion/styled` in your project: `npm install @emotion/styled` or `yarn add @emotion/styled`. -
TypeError: Cannot read properties of undefined (reading 'base')
cause Incorrect or incomplete `style` object passed to the `Treebeard` component, or `decorators` not provided.fixEnsure the `style` prop is a complete object matching the expected structure (or use the default `decorators` for baseline styling), and `decorators` are passed if you intend to use default visual components.
Warnings
- breaking Version 3.2.0 introduced a peer dependency update requiring React version 16.7.0 or greater. Projects using older React versions must upgrade React to use `react-treebeard` v3.2.0+.
- gotcha The `react-treebeard` package relies on `@emotion/styled` as a peer dependency for its styling system. If `@emotion/styled` is not installed in your project, or if there is a version mismatch, styling may not apply correctly or runtime errors may occur.
- gotcha When handling node toggles with the `onToggle` prop, it's common to mutate the `data` object directly as shown in some older examples. However, for proper React state management and to avoid unexpected re-renders or state inconsistencies, the `data` prop should be updated immutably within your component's state.
Install
-
npm install react-treebeard -
yarn add react-treebeard -
pnpm add react-treebeard
Imports
- Treebeard
const Treebeard = require('react-treebeard').Treebeard;import { Treebeard } from 'react-treebeard'; - decorators
import decorators from 'react-treebeard/lib/decorators';
import { decorators } from 'react-treebeard'; - Custom Styles (object)
import React from 'react'; import { Treebeard } from 'react-treebeard'; const customStyles = { /* ... */ }; const MyTree = () => ( <Treebeard data={/* ... */} style={customStyles} /> );
Quickstart
import React, { useState } from 'react';
import { Treebeard, decorators } from 'react-treebeard';
interface Node {
id: string;
name: string;
toggled?: boolean;
children?: Node[];
}
const initialData: Node = {
id: 'root',
name: 'root',
toggled: true,
children: [
{ id: 'parent1', name: 'Parent 1', children: [{ id: 'child1a', name: 'Child 1A' }, { id: 'child1b', name: 'Child 1B' }] },
{ id: 'parent2', name: 'Parent 2', toggled: true, children: [{ id: 'child2a', name: 'Child 2A' }] }
]
};
const customStyles = {
tree: {
base: { listStyle: 'none', backgroundColor: '#fdfdfd', margin: 0, padding: 0, color: '#333' },
node: {
base: { position: 'relative' },
link: { cursor: 'pointer', position: 'relative', padding: '0px 5px', display: 'block' },
activeLink: { background: '#e0e0e0' },
toggle: { base: { position: 'relative', display: 'inline-block', verticalAlign: 'top', marginLeft: '-5px', height: '24px', width: '24px' }, wrapper: { position: 'absolute', top: '50%', left: '50%', margin: '-7px 0 0 -7px', height: '14px', width: '14px' }, height: 14, width: 14, arrow: { fill: '#333', strokeWidth: 0 } },
header: { base: { display: 'inline-block', verticalAlign: 'top', color: '#333' }, connector: { width: '2px', height: '12px', borderLeft: 'solid 2px black', borderBottom: 'solid 2px black', position: 'absolute', top: '0px', left: '-21px' }, title: { lineHeight: '24px', verticalAlign: 'middle' } },
subtree: { listStyle: 'none', paddingLeft: '19px' },
loading: { color: '#E2C089' }
}
}
};
const MyTreeComponent: React.FC = () => {
const [treeData, setTreeData] = useState<Node>(initialData);
const onToggle = (node: Node, toggled: boolean) => {
const nextData = { ...treeData };
const findAndToggle = (currentNodes: Node[], targetNodeId: string): boolean => {
for (const n of currentNodes) {
if (n.id === targetNodeId) {
n.toggled = toggled;
return true;
}
if (n.children && findAndToggle(n.children, targetNodeId)) {
return true;
}
}
return false;
};
if (nextData.id === node.id) {
nextData.toggled = toggled;
} else if (nextData.children) {
findAndToggle(nextData.children, node.id);
}
setTreeData(nextData);
};
return (
<Treebeard
data={treeData}
onToggle={onToggle}
decorators={decorators}
style={customStyles}
/>
);
};
export default MyTreeComponent;