react-accessible-treeview vs react-sortable-tree vs react-treebeard
React Tree View Component Libraries
react-accessible-treeviewreact-sortable-treereact-treebeardSimilar Packages:

React Tree View Component Libraries

react-accessible-treeview, react-sortable-tree, and react-treebeard are React libraries designed to render hierarchical data as interactive tree structures in web applications. Each provides a different approach to visualizing and interacting with nested data, such as file systems, organizational charts, or category trees. While all three aim to solve similar problems, they differ significantly in their focus on accessibility, drag-and-drop capabilities, performance, and API design.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-accessible-treeview36,851323102 kB89 months agoMIT
react-sortable-tree04,975-3516 years agoMIT
react-treebeard01,692-727 years agoMIT

React Tree View Libraries Compared: Accessibility, Maintenance, and Performance

When you need to display hierarchical data in a React app — like folders, org charts, or nested categories — picking the right tree component library can make or break your user experience. react-accessible-treeview, react-sortable-tree, and react-treebeard each take a different path. Let’s compare them based on real engineering concerns: accessibility, maintenance status, interaction model, and performance.

⚠️ Critical First Step: Check Maintenance Status

Before diving into features, check if the library is still alive.

react-sortable-tree is deprecated. Both its npm page and GitHub repo state it’s archived and no longer maintained. Do not use it in new projects. It hasn’t kept up with React 18+ changes, and community forks are fragmented.

react-accessible-treeview and react-treebeard are actively maintained, with recent updates and responsive issue tracking.

🛑 If you’re maintaining a legacy app using react-sortable-tree, plan a migration. For new work, skip it entirely.

♿ Accessibility: Built-In vs. DIY

How well does each library support screen readers and keyboard navigation?

react-accessible-treeview is built from the ground up for accessibility. It follows WAI-ARIA practices for tree views, including proper roles (tree, treeitem, group), aria-expanded, aria-selected, and full keyboard support (arrow keys, Enter, Space).

// react-accessible-treeview: Accessible by default
import { TreeView, flattenTree } from 'react-accessible-treeview';

const data = [
  { id: 1, name: 'Documents', children: [
    { id: 2, name: 'Report.pdf' }
  ]}
];

<TreeView
  data={flattenTree(data)}
  aria-label="File explorer"
/>

react-treebeard does not implement ARIA patterns. You’ll need to add roles, labels, and keyboard handlers yourself if accessibility is required.

// react-treebeard: No built-in ARIA
import { Treebeard } from 'react-treebeard';

// You must manually add accessibility attributes
// (not shown in official examples)
<Treebeard data={data} />

react-sortable-tree had partial accessibility support but never achieved full compliance. Given its deprecated status, this is moot.

🖱️ Interaction Model: State Management and Customization

How do you control the tree and respond to user actions?

react-accessible-treeview uses a reducer-based state management model. You provide an initial state and handle actions like TOGGLE_NODE or SET_EXPANDED via a custom reducer. This gives fine-grained control but requires more setup.

// react-accessible-treeview: External state control
const [state, dispatch] = useReducer(treeReducer, {
  expandedIds: [],
  selectedIds: []
});

<TreeView
  data={flattenedData}
  {...state}
  onNodeToggle={(id) => dispatch({ type: 'TOGGLE_NODE', payload: id })}
/>

react-treebeard uses a functional, immutable approach. You pass the current data and an onToggle callback. When a node is toggled, you return a new data object with updated toggled flags.

// react-treebeard: Immutable updates
const [data, setData] = useState(initialData);

const onToggle = (node, toggled) => {
  const newData = toggleNode(data, node, toggled);
  setData(newData);
};

<Treebeard data={data} onToggle={onToggle} />

react-sortable-tree used a more imperative style with props like onChange that returned the entire modified tree. Again, this is historical context only.

🔄 Drag-and-Drop: Native Support or Not?

Need to reorder nodes by dragging?

react-accessible-treeview does not support drag-and-drop. You’d need to integrate a library like react-dnd yourself.

react-treebeard also does not include drag-and-drop.

react-sortable-tree was known for its robust drag-and-drop, but again — it’s deprecated.

💡 If drag-and-drop is essential, consider alternatives like @dnd-kit with a custom tree implementation, or evaluate whether your use case truly requires reordering.

🚀 Performance with Large Trees

How do they handle hundreds or thousands of nodes?

react-treebeard uses a recursive rendering model without virtualization. Performance degrades with very large trees, but it’s acceptable for moderate sizes (<500 nodes).

react-accessible-treeview renders only visible nodes by default (it flattens the tree internally and uses efficient mapping). This makes it more scalable for larger datasets while maintaining accessibility.

Neither library implements windowing (like react-window), so for extremely large trees (>10k nodes), you’d need a custom solution.

🎨 Styling and Theming

react-accessible-treeview provides minimal default styles and expects you to customize via CSS classes or styled-components. It exposes class names like .tree-node and .tree-node__label.

react-treebeard includes a default theme with smooth animations and supports custom themes via a theme prop object.

// react-treebeard: Custom theme
const customTheme = {
  tree: { base: { listStyle: 'none' } },
  node: { link: { color: 'blue' } }
};

<Treebeard data={data} theme={customTheme} />

✅ Summary: When to Use Which

Concernreact-accessible-treeviewreact-treebeardreact-sortable-tree
Maintenance✅ Actively maintained✅ Actively maintained❌ Deprecated
Accessibility✅ Full ARIA compliance❌ None built-in⚠️ Partial (historical)
Drag-and-Drop❌ Not supported❌ Not supported✅ Supported (but deprecated)
State Management🔧 Reducer-based (explicit)🔄 Immutable updates📜 Imperative (historical)
Performance✅ Efficient for large trees⚠️ Good for small/medium trees⚠️ Unknown (unmaintained)
Styling🎨 CSS classes🎨 Theme object🎨 CSS classes

💡 Final Recommendation

  • For accessibility-critical apps (government, healthcare, enterprise): choose react-accessible-treeview. The extra setup pays off in compliance and usability.
  • For lightweight, animated trees with moderate data (e.g., docs sidebar, static category picker): choose react-treebeard.
  • Avoid react-sortable-tree entirely in new codebases. If you’re stuck with it, migrate to one of the other two or build a custom solution using modern primitives.

Remember: a tree component seems simple until you need keyboard nav, screen reader support, or to handle 1,000 nodes. Pick the tool that matches your project’s real constraints — not just the demo screenshots.

How to Choose: react-accessible-treeview vs react-sortable-tree vs react-treebeard

  • react-accessible-treeview:

    Choose react-accessible-treeview if you need a fully accessible tree component that meets WCAG standards and supports keyboard navigation out of the box. It’s ideal for enterprise applications where accessibility compliance is non-negotiable, and you’re willing to manage state externally using a reducer pattern. Avoid it if you need built-in drag-and-drop or visual customization beyond basic styling.

  • react-sortable-tree:

    Do not choose react-sortable-tree for new projects — it is officially deprecated and no longer maintained. The npm page and GitHub repository clearly state it has been archived. While it once offered powerful drag-and-drop reordering and a rich feature set, its unmaintained status means it lacks compatibility with modern React versions (especially concurrent features) and may contain unpatched bugs or security issues.

  • react-treebeard:

    Choose react-treebeard if you prioritize performance with large datasets and prefer a minimal, functional API that encourages immutability. Its animated transitions and clean design work well for read-heavy trees like documentation navigators or static hierarchies. However, avoid it if you need native drag-and-drop, complex node interactions, or full accessibility support, as these are not part of its core offering.

README for react-accessible-treeview

SEEKING NEW MAINTAINERS

We're looking to give this repository to a new owner. Please see issue https://github.com/dgreene1/react-accessible-treeview/issues/222 to inquire about being the new owner.

react-accessible-treeview Build Status Greenkeeper badge npm version License: MIT

A react component that implements the treeview pattern as described by the WAI-ARIA Authoring Practices.

Features

  • Single and multiple selection.
  • Disabled nodes.
  • Extensive key bindings.
  • Highly customizable through the use of the render prop and prop getter patterns.
  • WAI-ARIA compliant.

Documentation and Demo

Prop Types

Prop nameTypeDefault valueDescription
dataarray[node]requiredTree data
nodeRendererfuncrequiredRender prop for the node (see below for more details)
onSelectfuncnoopFunction called when a node changes its selected state
onNodeSelectfuncnoopFunction called when a node was manually selected/deselected
onExpandfuncnoopFunction called when a node changes its expanded state
classNamestring""className to add to the outermost dom element, al ul with role = "tree"
multiSelectboolfalseAllows multiple nodes to be selected
propagateSelectboolfalseIf set to true, selecting a node will also select its descendants
propagateSelectUpwardsboolfalseIf set to true, selecting a node will update the state of its parent (e.g. a parent node in a checkbox will be automatically selected if all of its children are selected
propagateCollapseboolfalseIf set to true, collapsing a node will also collapse its descendants
expandOnKeyboardSelectboolfalseSelecting a node with a keyboard (using Space or Enter) will also toggle its expanded state
togglableSelectboolfalseWhether the selected state is togglable
defaultSelectedIdsarray[]Array with the ids of the default selected nodes. Unused if selectedIds is provided.
defaultExpandedIdsarray[]Array with the ids of the default expanded nodes. Unused if expandedIds is provided.
defaultDisabledIdsarray[]Array with the ids of the default disabled nodes
selectedIdsarray[](Controlled) Array with the ids that should be selected
expandedIdsarray[](Controlled) Array with the ids of branch node that should be expanded
clickActionenumSELECTAction to perform on click. One of: EXCLUSIVE_SELECT, FOCUS, SELECT
onBlurfuncnoopCustom onBlur event that is triggered when focusing out of the component as a whole (moving focus between the nodes won't trigger it).



data

An array of nodes. Nodes are objects with the following structure:

PropertyTypeDefaultDescription
idnumber or stringrequiredA nonnegative integer or string that uniquely identifies the node
namestringrequiredUsed to match on key press
childrenarray[id]requiredAn array with the ids of the children nodes.
parentidrequiredThe parent of the node. null for the root node
isBranchbooleanoptionalUsed to indicated whether a node is branch to be able load async data onExpand, default is false
metadataobjectoptionalUsed to add metadata into node object. We do not currently support metadata that is a nested object

The item with parent:null of the array represents the root node and won't be displayed.

Example:

const data = [
  { name: "", children: [1, 4, 9, 10, 11], id: 0, parent: null },
  { name: "src", children: [2, 3], id: 1, parent: 0 },
  { name: "index.js", id: 2, parent: 1 },
  { name: "styles.css", id: 3, parent: 1 },
  { name: "node_modules", children: [5, 7], id: 4, parent: 0 },
  { name: "react-accessible-treeview", children: [6], id: 5, parent: 4 },
  { name: "bundle.js", id: 6, parent: 5 },
  { name: "react", children: [8], id: 7, parent: 4 },
  { name: "bundle.js", id: 8, parent: 7 },
  { name: ".npmignore", id: 9, parent: 0 },
  { name: "package.json", id: 10, parent: 0 },
  { name: "webpack.config.js", id: 11, parent: 0 },
];

The array can also be generated from a nested object using the flattenTree helper (see the examples below). flattenTree preserves metadata.

Data supports non-sequential ids provided by user.



nodeRenderer

  • Arguments: An object containing the following properties:
PropertyTypeDescription
elementobjectThe object that represents the rendered node
getNodePropsfunctionA function which gives back the props to pass to the node
isBranchboolWhether the rendered node is a branch node
isSelectedboolWhether the rendered node is selected
isHalfSelectedbool or undefinedIf the node is a branch node, whether it is half-selected, else undefined
isExpandedbool or undefinedIf the node is a branch node, whether it is expanded, else undefined
isDisabledboolWhether the rendered node is disabled
levelnumberA positive integer that corresponds to the aria-level attribute
setsizenumberA positive integer that corresponds to the aria-setsize attribute
posinsetnumberA positive integer that corresponds to the aria-posinset attribute
handleSelectfunctionFunction to assign to the onClick event handler of the element(s) that will toggle the selected state
handleExpandfunctionFunction to assign to the onClick event handler of the element(s) that will toggle the expanded state
dispatchfunctionFunction to dispatch actions
treeStateobjectstate of the treeview



onSelect

  • Arguments: onSelect({element, isBranch, isExpanded, isSelected, isHalfSelected, isDisabled, treeState }) Note: the function uses the state after the selection.

onNodeSelect

  • Arguments: onNodeSelect({element, isBranch, isSelected, treeState }) Note: the function uses the state right after the selection before propagation.

onExpand

  • Arguments: onExpand({element, isExpanded, isSelected, isHalfSelected, isDisabled, treeState}) Note: the function uses the state after the expansion.

onLoadData

  • Arguments: onLoadData({element, isExpanded, isSelected, isHalfSelected, isDisabled, treeState}) Note: the function uses the state after inital data is loaded and on expansion.

Keyboard Navigation

Follows the same conventions described in https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-1/treeview-1b.html and https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-22.

KeyFunction
Enter or SpaceUpdates the selected node to the current node and triggers onSelect
Down Arrow
  • Moves focus to the next node that is tabbable without opening or closing a node.
  • If focus is on the last node, does nothing.
Up arrow
  • Moves focus to the previous node that is tabbable without opening or closing a node.
  • If focus is on the first node, does nothing.
Right Arrow
  • When focus is on a closed node, opens the node; focus does not move.
  • When focus is on an end node, does nothing.
  • When focus is on a open node, moves focus to the first child node.
Left Arrow
  • When focus is on an open node, closes the node.
  • When focus is on a child node that is also either an end node or a closed node, moves focus to its parent node.
  • When focus is on a root node that is also either an end node or a closed node, does nothing.
HomeMoves focus to first node without opening or closing a node.
EndMoves focus to the last node that can be focused without expanding any nodes that are closed.
a-z, A-Z
  • Focus moves to the next node with a name that starts with the typed character.
  • Search wraps to first node if a matching name is not found among the nodes that follow the focused node.
  • Search ignores nodes that are descendants of closed nodes.
* (asterisk)
  • Expands all closed sibling nodes that are at the same level as the focused node.
  • Focus does not move.
Shift + Down ArrowMoves focus to and toggles the selection state of the next node.
Shift + Up ArrowMoves focus to and toggles the selection state of the previous node.
Ctrl + ASelects all nodes in the tree. If all nodes are selected, unselects all nodes.



Mouse Navigation

KeyFunction
ClickToggles parent nodes and also performs one of clickActions = SELECT, EXCLUSIVE_SELECT, FOCUS
Ctrl+ClickIf multiselect is set to true, selects the node without dropping the current selected ones. If false, it selects the clicked node. Doesn't toggle parents.
Shift+ClickIf multiselect is set to true, selects from the node without dropping the current selected ones. If false, it focus the clicked node. Doesn't toggle parents.



Click actions

VariantFunction
SELECTSelects the clicked node (default).
EXCLUSIVE_SELECTSelects the clicked node and deselects the rest.
FOCUSFocuses the clicked node



treeState

The internal state of the component.

PropertyTypeDefaultDescription
selectedIdsSetnew Set(defaultSelectedIds)Set of the ids of the selected nodes
controlledIdsSetnew Set(controlledSelectedIds)Set of the ids of the nodes selected programmatically
tabbableIdnumberdata[0].children[0]Id of the node with tabindex = 0
isFocusedboolfalseWhether the tree has focus
expandedIdsSetnew Set(defaultExpandedIds)Set of the ids of the expanded nodes
halfSelectedIdsSetnew Set()Set of the ids of the selected nodes
lastUserSelectnumberdata[0].children[0]Last selection made directly by the user
lastInteractedWithnumber or nullnullLast node interacted with
lastManuallyTogglednumber or nullnullLast node that was manually selected/deselected
disabledIdsSetnew Set(defaultDisabledIds)Set of the ids of the selected nodes