This guide will walk you through creating a fully functional drag-and-drop tree from scratch.
Basic Setup
Import the component
Start by importing React Apple Tree and React hooks: import React , { useState } from 'react' ;
import ReactAppleTree from '@newtonschool/react-apple-tree' ;
Create your tree data
Define your initial tree structure. Each node can have a title, subtitle, expanded state, and nested children: const [ treeData , setTreeData ] = useState ([
{
id: 1 ,
title: 'Node 1' ,
children: [
{ id: 2 , title: 'Node 1.1' }
],
},
{
id: 3 ,
title: 'Node 2' ,
children: [
{ id: 4 , title: 'Node 2.1' },
{
id: 5 ,
title: 'Node 2.2' ,
children: [
{ id: 6 , title: 'Node 2.2.1' }
]
},
],
},
{
id: 7 ,
title: 'Node 3' ,
},
]);
The id field is optional but recommended for easier node identification.
Add a container with fixed height
React Apple Tree requires a container with a fixed height to enable virtualization: < div style = { { height: 400 } } >
{ /* Tree component goes here */ }
</ div >
Without a fixed height, the tree will not render properly. You can use any CSS unit (px, vh, rem, etc.).
Render the tree with required props
React Apple Tree requires three essential props: < ReactAppleTree
treeData = { treeData }
onChange = { ( newTreeData ) => setTreeData ( newTreeData ) }
getNodeKey = { ({ node }) => node . id }
/>
Required Props Explained:
treeData : Array of tree nodes representing your hierarchical data
onChange : Callback function that receives updated tree data after any modification
getNodeKey : Function that returns a unique identifier for each node
The getNodeKey function should return a stable, unique identifier. Using the node’s id is recommended, but you can use any unique property like title or a combination of properties.
Complete Working Example
Here’s the complete code for a basic interactive tree:
import React , { useState } from 'react' ;
import ReactAppleTree from '@newtonschool/react-apple-tree' ;
function MyTree () {
const [ treeData , setTreeData ] = useState ([
{
id: 1 ,
title: 'Node 1' ,
children: [
{ id: 2 , title: 'Node 1.1' }
],
},
{
id: 3 ,
title: 'Node 2' ,
children: [
{ id: 4 , title: 'Node 2.1' },
{
id: 5 ,
title: 'Node 2.2' ,
children: [
{ id: 6 , title: 'Node 2.2.1' }
]
},
],
},
{
id: 7 ,
title: 'Node 3' ,
},
]);
return (
< div style = { { height: 400 } } >
< ReactAppleTree
treeData = { treeData }
onChange = { ( newTreeData ) => setTreeData ( newTreeData ) }
getNodeKey = { ({ node }) => node . id }
/>
</ div >
);
}
export default MyTree ;
Understanding Tree Data Structure
Each node in your tree data is a TreeItem object with the following properties:
type TreeItem = {
// Optional unique identifier
id ?: string | number ;
// Primary label (required for display)
title ?: React . ReactNode ;
// Secondary label displayed below title
subtitle ?: React . ReactNode ;
// Controls whether children are visible (defaults to false)
expanded ?: boolean ;
// Nested child nodes
children ?: Array < TreeItem >;
// You can add any custom properties
[ key : string ] : any ;
};
Example with All Properties:
const richTreeData = [
{
id: 1 ,
title: 'Documents' ,
subtitle: '5 items' ,
expanded: true ,
icon: 'folder' ,
metadata: { size: '2.3 MB' },
children: [
{
id: 2 ,
title: 'Report.pdf' ,
subtitle: 'Last modified: Today' ,
icon: 'file' ,
},
],
},
];
You can extend the tree node with any custom properties you need. Access them through the node object in callbacks and custom renderers.
Interactive Features
With just these three props, you get:
Drag and Drop : Click and drag nodes to reorder or nest them
Expand/Collapse : Click the arrow icon to show or hide children
Keyboard Navigation : Use arrow keys to navigate the tree
Virtualization : Smooth performance even with thousands of nodes
Controlling Expansion State
To start with nodes expanded:
const [ treeData , setTreeData ] = useState ([
{
id: 1 ,
title: 'Parent Node' ,
expanded: true , // This node starts expanded
children: [
{ id: 2 , title: 'Visible Child' }
],
},
]);
Using Alternative Key Functions
If your nodes don’t have an id field, you can use other properties:
// Using title as key (ensure titles are unique)
getNodeKey = {({ node }) => node. title }
// Using tree index as key
getNodeKey = {({ treeIndex }) => treeIndex }
// Using a combination
getNodeKey = {({ node , treeIndex }) => ` ${ node . title } - ${ treeIndex } ` }
Using treeIndex alone may cause issues when nodes are reordered. It’s best to use a stable property like id or title.
Handling Changes
The onChange callback is called whenever the tree data changes:
const handleChange = ( newTreeData ) => {
console . log ( 'Tree updated:' , newTreeData );
setTreeData ( newTreeData );
// You can also persist to a backend here
// saveToBackend(newTreeData);
};
< ReactAppleTree
treeData = { treeData }
onChange = { handleChange }
getNodeKey = { ({ node }) => node . id }
/>
TypeScript Usage
If you’re using TypeScript, you can type your tree data:
import React , { useState } from 'react' ;
import ReactAppleTree , { TreeItem } from '@newtonschool/react-apple-tree' ;
interface MyNodeData {
id : number ;
title : string ;
icon ?: string ;
metadata ?: Record < string , any >;
}
function MyTree () {
const [ treeData , setTreeData ] = useState < Array < TreeItem < MyNodeData >>>([
{
id: 1 ,
title: 'Node 1' ,
icon: 'folder' ,
metadata: { color: 'blue' },
},
]);
return (
< div style = {{ height : 400 }} >
< ReactAppleTree < MyNodeData >
treeData = { treeData }
onChange = { setTreeData }
getNodeKey = {({ node }) => node. id }
/>
</ div >
);
}
Common Patterns
Empty Tree State
const [ treeData , setTreeData ] = useState ([]);
// The tree will render an empty state automatically
Loading Initial Data
const [ treeData , setTreeData ] = useState ([]);
const [ loading , setLoading ] = useState ( true );
useEffect (() => {
fetchTreeData (). then (( data ) => {
setTreeData ( data );
setLoading ( false );
});
}, []);
if ( loading ) return < div > Loading... </ div > ;
return (
< div style = { { height: 400 } } >
< ReactAppleTree
treeData = { treeData }
onChange = { setTreeData }
getNodeKey = { ({ node }) => node . id }
/>
</ div >
);
Next Steps
Now that you have a working tree, explore more advanced features:
Props Reference Explore all available props and customization options
Callbacks & Events Learn about event handlers and lifecycle callbacks
Helper Functions Use utility functions to manipulate tree data
Examples See practical examples and common use cases
Try It Out
Visit the Storybook to see interactive examples and experiment with different configurations.