Tree
A tree provides users with a way to navigate nested hierarchical information, with support for keyboard navigation and selection.
Content
Tree
follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items
prop, and a recursive function to render the children.
import {Tree, TreeItem} from './Tree';
import {Collection} from 'react-aria-components';
<Tree
aria-label="Files"
defaultExpandedKeys={[1, 4]}
items={items}
selectionMode="multiple">
{function renderItem(item) {
return (
<TreeItem title={item.title}>
{/* recursively render children */}
<Collection items={item.children}>
{renderItem}
</Collection>
</TreeItem>
);
}}
</Tree>
Asynchronous loading
Use renderEmptyState to display a spinner during initial load. To enable infinite scrolling, render a <TreeLoadMoreItem>
at the end of each <TreeItem>
. Use whatever data fetching library you prefer – this example uses useAsyncList
from react-stately
.
import {Collection, TreeLoadMoreItem} from 'react-aria-components';
import {Tree, TreeItem} from './Tree';
import {ProgressCircle} from './ProgressCircle';
import {useAsyncList} from 'react-stately';
interface Character {
name: string
}
function AsyncLoadingExample() {
return (
<Tree
aria-label="Async loading tree"
style={{height: 300}}>
<TreeItem title="Pokemon">
<Collection items={pokemonList.items}>
{(item) => <TreeItem id={item.name} title={item.name} />}
</Collection>
<TreeLoadMoreItem
onLoadMore={pokemonList.loadMore}
isLoading={pokemonList.loadingState === 'loadingMore'}>
<ProgressCircle isIndeterminate aria-label="Loading more..." />
</TreeLoadMoreItem>
</TreeItem>
<TreeItem title="Star Wars">
<Collection items={starWarsList.items}>
{(item) => <TreeItem id={item.name} title={item.name} />}
</Collection>
<TreeLoadMoreItem
onLoadMore={starWarsList.loadMore}
isLoading={starWarsList.loadingState === 'loadingMore'}>
<ProgressCircle isIndeterminate aria-label="Loading more..." />
</TreeLoadMoreItem>
</TreeItem>
</Tree>
);
}
Links
Use the href
prop on a <TreeItem>
to create a link. See the client side routing guide to learn how to integrate with your framework. Link interactions vary depending on the selection behavior. See the selection guide for more details.
Empty state
import {Tree} from './Tree';
<Tree
aria-label="Search results"
renderEmptyState={() => 'No results found.'}>
{[]}
</Tree>
Selection and actions
Use the selectionMode
prop to enable single or multiple selection. The selected items can be controlled via the selectedKeys
prop, matching the id
prop of the items. The onAction
event handles item actions. Items can be disabled with the isDisabled
prop. See the selection guide for more details.
Current selection:
Drag and drop
Tree supports drag and drop interactions when the dragAndDropHooks
prop is provided using the useDragAndDrop hook. Users can drop data on the list as a whole, on individual items, insert new items between existing ones, or reorder items. React Aria supports drag and drop via mouse, touch, keyboard, and screen reader interactions. See the drag and drop guide to learn more.
import {useTreeData} from 'react-stately';
import {Tree, TreeItem} from './Tree';
import {useDragAndDrop, Collection} from 'react-aria-components';
function Example() {
let {dragAndDropHooks} = useDragAndDrop({
getItems: (keys) => [...keys].map(key => ({'text/plain': tree.getItem(key).value.title})),
onMove(e) {
if (e.target.dropPosition === 'before') {
tree.moveBefore(e.target.key, e.keys);
} else if (e.target.dropPosition === 'after') {
tree.moveAfter(e.target.key, e.keys);
} else if (e.target.dropPosition === 'on') {
// Move items to become children of the target
let targetNode = tree.getItem(e.target.key);
if (targetNode) {
let targetIndex = targetNode.children ? targetNode.children.length : 0;
let keyArray = Array.from(e.keys);
for (let i = 0; i < keyArray.length; i++) {
tree.move(keyArray[i], e.target.key, targetIndex + i);
}
}
}
}
});
return (
<Tree
aria-label="Tree with hierarchical drag and drop"
selectionMode="multiple"
items={tree.items}
dragAndDropHooks={dragAndDropHooks}
>
{function renderItem(item) {
return (
<TreeItem title={item.value.title}>
<Collection items={item.children}>
{renderItem}
</Collection>
</TreeItem>
)
}}
</Tree>
);
}