DEV Community

Cover image for Build tree view with react-arborist [part 1]
Ihor Filippov
Ihor Filippov

Posted on

Build tree view with react-arborist [part 1]

If simply reading this tutorial isn't enough for you, you can go here and write code alongside it.


Introduction

React Arborist is a powerful React library designed for building interactive tree components with virtualization support. Unlike simple nested lists, react-arborist provides advanced features like drag-and-drop, multi-selection, inline editing, and most importantly - virtualization for handling large datasets efficiently.

In this tutorial series, we'll explore:

  • Part 1: Basic setup and simple tree rendering.
  • Part 2: Adding interactivity with selection, editing and deletion.
  • Part 3: Search and custom styling.

Why React Arborist?

Traditional tree implementations often struggle with performance when dealing with large datasets. React Arborist solves this by:

  • Virtualization: Only renders visible nodes, enabling smooth scrolling through thousands of items
  • Built-in interactions: Drag-and-drop, selection, and editing work out of the box
  • Flexible data structure: Works with any hierarchical data format
  • Customizable rendering: Full control over how nodes appear and behave

Setup local environment

npm create vite@latest my-arborist-app -- --template react-ts
cd my-arborist-app
npm install
Enter fullscreen mode Exit fullscreen mode
npm install react-arborist
Enter fullscreen mode Exit fullscreen mode

and run the project

npm run dev
Enter fullscreen mode Exit fullscreen mode

Basic Tree Structure

Let's start with a simple file system example. React Arborist expects your data to have a specific structure with id, name, and optional children properties:

/src/types.ts

export type FileNodeType = "folder" | "file";

export interface FileNode {
  id: string;
  name: string;
  type?: FileNodeType;
  children?: FileNode[];
}
Enter fullscreen mode Exit fullscreen mode

Here we added an additional type, FileNodeType, to distinguish a file from a folder. We will need it in the future.

Let's add some data to work with.
/src/data.ts

import type { FileNode } from "./types";

const data: FileNode[] = [
  {
    id: "1",
    name: "Documents",
    type: "folder",
    children: [
      {
        id: "2",
        name: "Projects",
        type: "folder",
        children: [
          { id: "3", name: "Website Redesign.pdf", type: "file" },
          { id: "4", name: "Mobile App Mockups.sketch", type: "file" },
        ],
      },
      { id: "5", name: "Resume.docx", type: "file" },
      { id: "6", name: "Cover Letter.pdf", type: "file" },
    ],
  },
  {
    id: "7",
    name: "Pictures",
    type: "folder",
    children: [
      { id: "8", name: "Vacation 2023", type: "folder" },
      { id: "9", name: "Family Photos", type: "folder" },
    ],
  },
  { id: "10", name: "Downloads", type: "file" },
];

export default data;
Enter fullscreen mode Exit fullscreen mode

Now, it is time to create our first component with minimal configuration.
/src/FileExplorer

import React from 'react';
import { Tree } from 'react-arborist';
import data from './data';

const FileExplorer: React.FC = () => {
  return (
    <div>
      <Tree initialData={data}>
        {({ node, style, dragHandle }) => (
          <div style={style} ref={dragHandle}>
            {node.data.name}
          </div>
        )}
      </Tree>
    </div>
  );
};

export default FileExplorer;
Enter fullscreen mode Exit fullscreen mode

And import it to App component.
App.tsx

import FileExplorer from "./src/FileExplorer";

export default function App(): JSX.Element {
  return <FileExplorer />;
}
Enter fullscreen mode Exit fullscreen mode

By default, we only need to provide the initialData prop to render a functional Tree component.

If you've done everything correctly, a basic representation of our file tree should appear at the screen

Basic Styling, Icons and Configurations

Props enable us to manage how a component behaves. The Tree component provides a variety of useful props that we can use to customize it.

In our example, we'll pass the following props:

These settings are quite intuitive. The width and height define the size of the widget, while indent determines the spacing for nested items. The openByDefault prop determines whether the tree is expanded or collapsed when it first renders.

/src/FileExplorer.tsx

const FileExplorer: React.FC = () => {
  return (
    <div>
      <Tree
        initialData={data}
        width={500}
        height={500}
        indent={34}
        openByDefault={false}
      >
        {({ node, style, dragHandle }) => (
          <div style={style} ref={dragHandle}>
            {node.data.name}
          </div>
        )}
      </Tree>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now let's add icons to visually distinguish files from folders.

/src/FileExplorer.tsx

import React from "react";
import { Tree } from "react-arborist";
import { File, Folder } from "lucide-react";
import data from "./data";

const FileExplorer: React.FC = () => {
  return (
    <div>
      <Tree
        initialData={data}
        width={400}
        height={500}
        indent={34}
        rowHeight={34}
        openByDefault={true}
      >
        {({ node, style, dragHandle }) => (
          <div
            style={{
              display: "flex",
              alignItems: "center",
              ...style,
            }}
            ref={dragHandle}
          >
            <span style={{ marginRight: "8px" }}>
              {node.isLeaf ? <File /> : <Folder />}
            </span>
            <span>{node.data.name}</span>
          </div>
        )}
      </Tree>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Here we added icons from the lucide-react library. We also introduced a new prop, rowHeight, to ensure the icons don’t visually overlap with neighboring items. Additionally, we use the node.isLeaf property to determine whether a node is a file or a folder.

to be continued...

Top comments (0)