react-tree
a hierarchical tree component for React in Typescript
Features
- Written in Typescript will full typings exported from package
- Theming support for almost all parts of the components appearance, (NEW including partial theming) (see Theming below)
- Use as an uncontrolled component with
defaultSelectedNodes
anddefaultOpenNodes
or a completely controlled component withselectedNodes
andopenNodes
props withonEvent
listeners - Fully stylable container for fixed width, or flex-box based layouts, or scrollable container when lists are too long for the parent container
- Optimized UX to clearly indicate open/closed folders, selected items and feedback on user input
- Toggle support for long-object labels with
truncateLongText
prop - Title attributes on hover for truncated labels that are too long for container
- Toggle support for empty folders with
displayEmpty
prop - Customizable component message strings with
messages
prop (no data, empty folders, loading) - Display a loading indicator and nothing when in loading state with
loading
prop - Opt-in animated micro-interactions for opening/closing folders
- Multi-select API! hold your OS’s
meta
key orctrl
key to be able to select/deselect multiple-nodes - NEW in v3 imperative API via export
useReactTreeApi
hook. Pass the ref to the componenta (see Imperative API below - NEW in v3 new context-based state management for better maintainability and handling of business logic
- NEW in v3 moved
react-dom
andstyled-components
topeerDependencies
- NEW in v3 Custom render functions for nodes and icons (full node context passed to render function with open/selected status)
Add to a project
yarn add @naisutech/react-tree
or npm install @naisutech/react-tree
Usage
There is only one required prop: nodes
(see Data format)
import { ReactTree } from '@naisutech/react-tree' // component code const data = ... // fetch data <ReactTree nodes={data} />
Data format
- data should be a flat list of node
objects
with required properties: label
,id
,parentId
- optional properties:
items
id
is typed to benumber
orstring
- root nodes should have
parentId
property set tonull
- files/leaf items should be a flat list of node objects on
items
property inside a node. - files do not require an
items
property (this should be obvious) - example:
[ { "id": 12345678, "parentId": null, "label": "My parent node", "items": [ { "id": 87654321, "label": "My file", "parentId": 12345678 } ] }, { "id": 56789012, "parentId": 12345678, "label": "My child node" } ]
Component API
There are a number of optional properties which can be used to customise the UX of your React Tree component. You can explore the full interactive docs here or you can refer to the sample code below:
<ReactTree nodes: TreeNodeList defaultOpenNodes?: TreeNodeId[] defaultSelectedNodes?: TreeNodeId[] messages?: { noData?: React.ReactNode; loading?: React.ReactNode; emptyItems?: React.ReactNode } loading?: boolean theme?: string themes?: ThemeSettings enableItemAnimations?: boolean enableIndicatorAnimations?: boolean showEmptyItems?: boolean noIcons?: boolean truncateLongText?: boolean containerStyles?: React.CSSProperties RenderNode?: TreeRenderFn RenderIcon?: TreeRenderFn selectedNodes?: TreeNodeId[] openNodes?: TreeNodeId[] onToggleSelectedNodes?: (nodes: TreeNodeId[]) => void onToggleOpenNodes?: (nodes: TreeNodeId[]) => void ref: React.MutableRef<ReactTreeApi> />
Props list
Prop nameProp typeDefaultRequiredDescriptionnodesTreeNodeList[]
YThe data set for react tree to renderdefaultOpenNodesTreeNodeId[]undefined
NThe default set of open nodes. Specify when you intend to use the component in uncontrolled modedefaultSelectedNodesTreeNodeId[]undefined
NThe default set of selected nodes. Specify when you intend to use the component in uncontrolled modeopenNodesTreeNodeId[]undefined
NThe currently open nodes. Specify when you intend to use the component in controlled mode.selectedNodesTreeNodeId[]undefined
NThe currently selected nodes. Specify when you intend to use the component in controlled mode.themestringlight
NThe curently selected theme (built-in themes are light
, and dark
)themesThemeSettings
(Record<string, ReactTreeTheme>
){}
NThe user-specified set of themesloadingbooleanfalse
NDisplay a loader instead of the rendered treemessages{ noData?: React.ReactNode; loading?: React.ReactNode; emptyItems?: React.ReactNode }{loading: 'Loading...', noData: 'No data to render 😔', emptyItems: '[Empty]' }
NThe default component message strings.enableItemAnimationsbooleanfalse
NWhether or not to animate folders on enter/exitenableIndicatorAnimationsbooleanfalse
NWhether or not to animate folder open/close iconsshowEmptyItemsbooleanfalse
NWhether or not to display an indicator for empty foldersnoIconsbooleanfalse
NDisable the icon displaytruncateLongTextbooleanfalse
NPrepares all DOM nodes to be able to truncate long text nodes. Note this setting will have no effect if container is not styled to have a fixed width.multiSelectbooleanfalse
NComponent is single select by default.containerStylesCSSProperties{}
NStyle the React Tree containerRenderNodeTreeRenderFnundefined
NA custom renderer for Node
elements. See Custom renderingRenderIconTreeRenderFnundefined
NA custom renderer for Icon
elements. See Custom renderingonToggleSelectedNodes(nodes: TreeNodeId[]) => void
() => voidNA callback called whenever items are selected/deselectedonToggleOpenNodes(nodes: TreeNodeId[]) => void
() => voidNA callback called whenever items are opened/closed
Typescript
React Tree is written in typescript and is fully typescript compatible. All type definitions are exported directly from the library. See src/types
in the repo for extensive definitions
Imperative API
React Tree exposes a hook useReactTreeApi
which you can use to imperatively control the tree component. The hook returns a React.MutableRef<ReactTreeApi>
type object. All you have to do is pass the returned object to the ref
props on the React Tree component. The .current
property on the ref object will be populated with the API functions you need to fully control the component.
Usage
import ReactTree, { useReactTreeApi } from "@naisutech/react-tree" const App = () => { const treeApi = useReactTreeApi() return <div> <button onClick={() => { treeApi.current.toggleAllNodesOpenState("open") }}>Expand all</button> <ReactTree nodes={[]} ref={treeApi} /> </div> }
Docs
Full details of the React Tree API:
interface ReactTreeApi { getOpenNodes: () => (number | string)[] // get a list of all open nodes getSelectedNodes: () => (number | string)[] // get a list of all selected nodes toggleNodeSelectedState: (node: string | number) => void // toggle a node selected/unselected. This is an inclusive operation (all other selected nodes are retained) toggleNodeOpenState: (node: string | number) => void // toggle a node open. This is an inclusive operation (all other open nodes are retained) toggleAllNodesOpenState: (state: 'open' | 'closed') => void // open or close all nodes toggleAllNodesSelectedState: (state: 'selected' | 'unselected') => void // select or deselect all nodes toggleOpenNodes: (nodes: (number | string)[]) => void // toggle a set of nodes open. This is an additive operation (it's a union of already open and newly open nodes) toggleClosedNodes: (nodes: (number | string)[]) => void // toggle a set of nodes closed. This is an subtractive operation (it's a difference of already open and newly closed nodes) toggleOpenClosedNodes: (nodes: (number | string)[]) => void // toggle a set of nodes open. This is an exclusive operation (all other open nodes are closed) selectNodes: (nodes: (number | string)[]) => void // toggle a set of nodes selected. This is an additive operation (it's a union of already selected and newly selected nodes) deselectNodes: (nodes: (number | string)[]) => void // toggle a set of nodes deselected. This is an subtractive operation (it's a difference of already selected and newly deselected nodes) toggleSelectedNodes: (nodes: (number | string)[]) => void // toggle a set of nodes selected. This is an exclusive operation (all other selected nodes are deselected) }
Theming
ReactTree supports custom theming. All values are CSS compatible properties. Provide the custom theme object to the themes
prop (with a key matching your theme name) and provide your theme name to the theme
prop.
Theme API:
Note all props are optional. Any props not provided will use initial
settings (e.g. background-color: initial;
)
interface ReactTreeTheme { text?: { fontSize?: SizeUnit | CSSUnit fontFamily?: string color?: string selectedColor?: string | null hoverColor?: string | null } nodes?: { height?: CSSUnit folder?: { bgColor?: string selectedBgColor?: string hoverBgColor?: string | null } leaf?: { bgColor?: string selectedBgColor?: string hoverBgColor?: string | null } separator?: { border?: string borderColor?: string } icons?: { size?: CSSUnit folderColor?: string leafColor?: string } } }
Usage:
const myThemes: ThemeSettings = { { "exampleCustomTheme": { "text": { "fontSize": "xl", "fontFamily": "cursive", "color": "#fafafa", "selectedColor": "#fafafa", "hoverColor": "#fafafa" }, "nodes": { "height": "3.5rem", "folder": { "bgColor": "gold", "selectedBgColor": "goldenrod", "hoverBgColor": "yellow" }, "leaf": { "bgColor": "magenta", "selectedBgColor": "blueviolet", "hoverBgColor": "violet" }, "separator": { "border": "3px solid", "borderColor": "transparent" }, "icons": { "size": "1rem", "folderColor": "crimson", "leafColor": "white" } } } } } <Tree nodes={data} theme="exampleCustomTheme" themes={myThemes} />
Result
Custom render functions
React Tree includes two props RenderNode
and RenderIcon
which can be used to fully customize the appearance and behaviour of the component
### API
Icons and nodes both use the same API, TreeRenderFn
:
type TreeRenderFn = ({ node, type, selected = false, open = false, context }: { node: TreeNode type: 'leaf' | 'node' | 'loader' selected: boolean open?: boolean Icon?: React.ReactNode context: TReactTreeContext }) => React.ReactNode
node: TreeNode
– the node datatype: 'leaf' | 'node' | 'loader'
– the type of this nodeselected: boolean
– indicates whether node is selected or notopen: boolean
– use only for node, indicates whether node is open or noticon: React.ReactNode
– the SVG component of the original React Tree icon if you want to use itcontext: TReactTreeContext
– the entire React Tree context including the state, and API methods
Nodes
ReactTree will call (if provided) the RenderNode
icon with the API for TreeRenderFn. This should be enough information to render any customization you need.
N.B. if you use the prop truncateLongText
you’ll notice that unless you properly style your custom node elements, it will take no effect. As explained elsewhere, you’ll need to make sure that a) the container is styled to have a fixed width and b) that the custom node is styled overflow-x: hidden;
and text-overflow: ellipsis
to be rendered correctly.
Icons
ReactTree comes with a pretty solid set of default icons for showing node elements and leaf elements. However, if you want to hide the icons, pass the noIcons
prop.
If you want to customize the icons, you can! Some conditions:
- the icon is rendered inside a container which whose size is determined by theme property
icons.size
. It will render any child SVG orimg
element as100%
height and width within that container
TODO in v4 and beyond
- add drag and drop support
Contributing
- open issues and PRs and we’ll work together!