Skip to main content
This guide will walk you through creating a fully functional drag-and-drop tree from scratch.

Basic Setup

1

Import the component

Start by importing React Apple Tree and React hooks:
import React, { useState } from 'react';
import ReactAppleTree from '@newtonschool/react-apple-tree';
2

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.
3

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

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.

Build docs developers (and LLMs) love