Skip to main content

Simple Tree

The most basic usage requires three props: treeData, onChange, and getNodeKey.
import React, { useState } from 'react';
import ReactAppleTree from '@newtonschool/react-apple-tree';

const BasicTree = () => {
  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>
  );
};
Always wrap your tree in a container with a defined height. The tree uses virtualization and requires a height to render properly.

Tree Data Structure

Each node in the tree can have these properties:
  • title - The primary label (string or React element)
  • subtitle - Secondary label (optional)
  • expanded - Whether children are visible (defaults to false)
  • children - Array of child nodes (optional)
  • Custom properties - Add any additional data your app needs
const treeData = [
  {
    id: 1,
    title: 'Parent Node',
    subtitle: 'This is a subtitle',
    expanded: true,
    customData: { type: 'folder', icon: '📁' },
    children: [
      {
        id: 2,
        title: 'Child Node',
        customData: { type: 'file', icon: '📄' },
      },
    ],
  },
];

Handling Tree Changes

The onChange callback is called whenever the tree structure changes through drag-and-drop operations.
const [treeData, setTreeData] = useState(initialData);

const handleChange = (newTreeData) => {
  console.log('Tree updated:', newTreeData);
  setTreeData(newTreeData);
  
  // Optional: Save to backend
  // saveToAPI(newTreeData);
};

return (
  <ReactAppleTree
    treeData={treeData}
    onChange={handleChange}
    getNodeKey={({ node }) => node.id}
  />
);
Use the onChange callback to sync tree changes with your backend or application state.

Using getNodeKey

The getNodeKey function provides a unique identifier for each node. This is crucial for tracking nodes during drag-and-drop operations.
getNodeKey={({ node }) => node.id}
For best results, use a stable, unique identifier like node.id rather than treeIndex, which can change when nodes are moved.

Custom Node Content

You can render custom content in your nodes using React elements for title and subtitle:
const treeData = [
  {
    id: 1,
    title: (
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span>📁</span>
        <strong>Documents</strong>
      </div>
    ),
    subtitle: <span style={{ color: '#666' }}>3 items</span>,
    children: [
      {
        id: 2,
        title: (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span>📄</span>
            <span>Report.pdf</span>
          </div>
        ),
      },
    ],
  },
];

Controlling Expansion

Control which nodes are expanded using the expanded property:
const [treeData, setTreeData] = useState([
  {
    id: 1,
    title: 'Expanded Parent',
    expanded: true, // Children will be visible
    children: [
      { id: 2, title: 'Visible Child' },
    ],
  },
  {
    id: 3,
    title: 'Collapsed Parent',
    expanded: false, // Children will be hidden
    children: [
      { id: 4, title: 'Hidden Child' },
    ],
  },
]);
You can programmatically expand/collapse nodes using the helper function:
import ReactAppleTree, { toggleExpandedForAll } from '@newtonschool/react-apple-tree';

const expandAll = () => {
  setTreeData(toggleExpandedForAll({ treeData, expanded: true }));
};

const collapseAll = () => {
  setTreeData(toggleExpandedForAll({ treeData, expanded: false }));
};

Tracking Node Events

Listen to node expansion and drag events:
<ReactAppleTree
  treeData={treeData}
  onChange={setTreeData}
  getNodeKey={({ node }) => node.id}
  onVisibilityToggle={({ node, expanded, treeData }) => {
    console.log(`Node "${node.title}" is now ${expanded ? 'expanded' : 'collapsed'}`);
  }}
  onMoveNode={({ node, prevPath, nextPath, treeData }) => {
    console.log(`Node "${node.title}" moved from`, prevPath, 'to', nextPath);
  }}
  onDragStateChanged={({ isDragging, draggedNode }) => {
    if (isDragging) {
      console.log(`Started dragging "${draggedNode.title}"`);
    } else {
      console.log('Drag ended');
    }
  }}
/>

Next Steps

Now that you understand the basics, explore:

Build docs developers (and LLMs) love