beta

TreeView

A tree view provides users with a way to navigate nested hierarchical information.

installyarn add @react-spectrum/tree
version3.0.0-beta.1
usageimport {TreeView, TreeViewItem} from '@react-spectrum/tree'

Example#


<TreeView
  aria-label="Example tree with static contents"
  defaultExpandedKeys={new Set(['documents', 'photos'])}
  height="size-4600"
  maxWidth="size-6000"
>
  <TreeViewItem id="documents" textValue="Documents">
    <Text>Documents</Text>
    <Folder />
    <TreeViewItem id="project-a" textValue="Project A">
      <Text>Project A</Text>
      <Folder />
      <TreeViewItem id="weekly-report" textValue="Weekly-Report">
        <Text>Weekly Report</Text>
        <FileTxt />
      </TreeViewItem>
    </TreeViewItem>
    <TreeViewItem id="document-1" textValue="Document 1">
      <Text>Document 1</Text>
      <FileTxt />
    </TreeViewItem>
    <TreeViewItem id="document-2" textValue="Document 2">
      <Text>Document 2</Text>
      <FileTxt />
    </TreeViewItem>
  </TreeViewItem>
  <TreeViewItem id="photos" textValue="Photos">
    <Text>Photos</Text>
    <Folder />
    <TreeViewItem id="image-1" textValue="Image 1">
      <Text>Image 1</Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem id="image-2" textValue="Image 2">
      <Text>Image 2</Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem id="image-3" textValue="Image 3">
      <Text>Image 3</Text>
      <Image />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>
<TreeView
  aria-label="Example tree with static contents"
  defaultExpandedKeys={new Set(['documents', 'photos'])}
  height="size-4600"
  maxWidth="size-6000"
>
  <TreeViewItem id="documents" textValue="Documents">
    <Text>Documents</Text>
    <Folder />
    <TreeViewItem id="project-a" textValue="Project A">
      <Text>Project A</Text>
      <Folder />
      <TreeViewItem
        id="weekly-report"
        textValue="Weekly-Report"
      >
        <Text>Weekly Report</Text>
        <FileTxt />
      </TreeViewItem>
    </TreeViewItem>
    <TreeViewItem id="document-1" textValue="Document 1">
      <Text>Document 1</Text>
      <FileTxt />
    </TreeViewItem>
    <TreeViewItem id="document-2" textValue="Document 2">
      <Text>Document 2</Text>
      <FileTxt />
    </TreeViewItem>
  </TreeViewItem>
  <TreeViewItem id="photos" textValue="Photos">
    <Text>Photos</Text>
    <Folder />
    <TreeViewItem id="image-1" textValue="Image 1">
      <Text>Image 1</Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem id="image-2" textValue="Image 2">
      <Text>Image 2</Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem id="image-3" textValue="Image 3">
      <Text>Image 3</Text>
      <Image />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>
<TreeView
  aria-label="Example tree with static contents"
  defaultExpandedKeys={new Set(
    [
      'documents',
      'photos'
    ]
  )}
  height="size-4600"
  maxWidth="size-6000"
>
  <TreeViewItem
    id="documents"
    textValue="Documents"
  >
    <Text>
      Documents
    </Text>
    <Folder />
    <TreeViewItem
      id="project-a"
      textValue="Project A"
    >
      <Text>
        Project A
      </Text>
      <Folder />
      <TreeViewItem
        id="weekly-report"
        textValue="Weekly-Report"
      >
        <Text>
          Weekly Report
        </Text>
        <FileTxt />
      </TreeViewItem>
    </TreeViewItem>
    <TreeViewItem
      id="document-1"
      textValue="Document 1"
    >
      <Text>
        Document 1
      </Text>
      <FileTxt />
    </TreeViewItem>
    <TreeViewItem
      id="document-2"
      textValue="Document 2"
    >
      <Text>
        Document 2
      </Text>
      <FileTxt />
    </TreeViewItem>
  </TreeViewItem>
  <TreeViewItem
    id="photos"
    textValue="Photos"
  >
    <Text>Photos</Text>
    <Folder />
    <TreeViewItem
      id="image-1"
      textValue="Image 1"
    >
      <Text>
        Image 1
      </Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem
      id="image-2"
      textValue="Image 2"
    >
      <Text>
        Image 2
      </Text>
      <Image />
    </TreeViewItem>
    <TreeViewItem
      id="image-3"
      textValue="Image 3"
    >
      <Text>
        Image 3
      </Text>
      <Image />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>

Content#


TreeView is a collection component that provides users with a way to navigate nested hierarchical information.

Basic usage of TreeView, seen in the example above, shows the use of a static collection where the contents of the TreeView are hard coded. Dynamic collections, as shown below, can be used when the options come from an external data source, such as an API, or update over time. Providing the data in this way allows TreeView to automatically cache the rendering of each item, which dramatically improves performance.

Items can be statically defined as children, or generated dynamically using a function based on the data passed to the items prop.

Each item has a unique key defined by the data. The key of each item element is implicitly defined by the id property of the item object. See collections to learn more about keys in dynamic collections.

Note: Asynchronous tree loading (i.e, loading by level or infinite scrolling) is not yet supported in TreeView.
type MyItem = {
  id: string;
  name: string;
  icon: JSX.Element;
  childItems?: MyItem[];
};

let items: MyItem[] = [
  {
    id: 'projects',
    name: 'Projects',
    icon: <Folder />,
    childItems: [
      { id: 'project-1', name: 'Project 1', icon: <FileTxt /> },
      {
        id: 'project-2',
        name: 'Project 2',
        icon: <Folder />,
        childItems: [
          { id: 'document-a', name: 'Document A', icon: <FileTxt /> },
          { id: 'document-b', name: 'Document B', icon: <FileTxt /> }
        ]
      }
    ]
  },
  {
    id: 'reports',
    name: 'Reports',
    icon: <Folder />,
    childItems: [
      { id: 'report-1', name: 'Reports 1', icon: <FileTxt /> }
    ]
  }
];

function ExampleTree(props) {
  return (
    <TreeView
      aria-label="Example tree with dynamic content"
      height="size-3000"
      maxWidth="size-6000"
      items={items}
      {...props}
    >
      {(item: MyItem) => (
        <TreeViewItem childItems={item.childItems} textValue={item.name}>
          <Text>{item.name}</Text>
          {item.icon}
        </TreeViewItem>
      )}
    </TreeView>
  );
}
type MyItem = {
  id: string;
  name: string;
  icon: JSX.Element;
  childItems?: MyItem[];
};

let items: MyItem[] = [
  {
    id: 'projects',
    name: 'Projects',
    icon: <Folder />,
    childItems: [
      {
        id: 'project-1',
        name: 'Project 1',
        icon: <FileTxt />
      },
      {
        id: 'project-2',
        name: 'Project 2',
        icon: <Folder />,
        childItems: [
          {
            id: 'document-a',
            name: 'Document A',
            icon: <FileTxt />
          },
          {
            id: 'document-b',
            name: 'Document B',
            icon: <FileTxt />
          }
        ]
      }
    ]
  },
  {
    id: 'reports',
    name: 'Reports',
    icon: <Folder />,
    childItems: [
      {
        id: 'report-1',
        name: 'Reports 1',
        icon: <FileTxt />
      }
    ]
  }
];

function ExampleTree(props) {
  return (
    <TreeView
      aria-label="Example tree with dynamic content"
      height="size-3000"
      maxWidth="size-6000"
      items={items}
      {...props}
    >
      {(item: MyItem) => (
        <TreeViewItem
          childItems={item.childItems}
          textValue={item.name}
        >
          <Text>{item.name}</Text>
          {item.icon}
        </TreeViewItem>
      )}
    </TreeView>
  );
}
type MyItem = {
  id: string;
  name: string;
  icon: JSX.Element;
  childItems?: MyItem[];
};

let items: MyItem[] = [
  {
    id: 'projects',
    name: 'Projects',
    icon: <Folder />,
    childItems: [
      {
        id: 'project-1',
        name:
          'Project 1',
        icon: (
          <FileTxt />
        )
      },
      {
        id: 'project-2',
        name:
          'Project 2',
        icon: <Folder />,
        childItems: [
          {
            id:
              'document-a',
            name:
              'Document A',
            icon: (
              <FileTxt />
            )
          },
          {
            id:
              'document-b',
            name:
              'Document B',
            icon: (
              <FileTxt />
            )
          }
        ]
      }
    ]
  },
  {
    id: 'reports',
    name: 'Reports',
    icon: <Folder />,
    childItems: [
      {
        id: 'report-1',
        name:
          'Reports 1',
        icon: (
          <FileTxt />
        )
      }
    ]
  }
];

function ExampleTree(
  props
) {
  return (
    <TreeView
      aria-label="Example tree with dynamic content"
      height="size-3000"
      maxWidth="size-6000"
      items={items}
      {...props}
    >
      {(
        item: MyItem
      ) => (
        <TreeViewItem
          childItems={item
            .childItems}
          textValue={item
            .name}
        >
          <Text>
            {item.name}
          </Text>
          {item.icon}
        </TreeViewItem>
      )}
    </TreeView>
  );
}

Internationalization#

To internationalize a TreeView, all text content within the TreeView should be localized. This includes the aria-label provided to the TreeView if any. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TreeView is automatically flipped.

Labeling#


Accessibility#

An aria-label must be provided to the TreeView for accessibility. If the TreeView is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Expansion#


By default, TreeView items are initially collapsed. Use defaultExpandedKeys to provide a default set of expanded items. Note that the value of the expanded keys must match the id prop of the TreeViewItem.

The example below uses defaultExpandedKeys to select the items with keys "documents" and "photos".

<ExampleTree
  aria-label="Example tree with default expanded items"
  defaultExpandedKeys={['projects', 'reports']}/>
<ExampleTree
  aria-label="Example tree with default expanded items"
  defaultExpandedKeys={['projects', 'reports']}/>
<ExampleTree
  aria-label="Example tree with default expanded items"
  defaultExpandedKeys={[
    'projects',
    'reports'
  ]}/>

Controlled expansion#

To programmatically control item expansion, use the expandedKeys prop paired with the onExpandedChange callback. The key prop from the expanded items will be passed into the callback when the item is pressed, allowing you to update state accordingly.

Here is how you would control expansion for the above example.

function ControlledExpansion() {
  let [expandedKeys, setExpandedKeys] = React.useState<Set<Key>>(
    new Set(['projects', 'reports'])
  );

  return (
    <ExampleTree
      aria-label="Example tree with controlled expanded items"
      expandedKeys={expandedKeys}
      onExpandedChange={setExpandedKeys}    />
  );
}
function ControlledExpansion() {
  let [expandedKeys, setExpandedKeys] = React.useState<
    Set<Key>
  >(new Set(['projects', 'reports']));

  return (
    <ExampleTree
      aria-label="Example tree with controlled expanded items"
      expandedKeys={expandedKeys}
      onExpandedChange={setExpandedKeys}    />
  );
}
function ControlledExpansion() {
  let [
    expandedKeys,
    setExpandedKeys
  ] = React.useState<
    Set<Key>
  >(
    new Set([
      'projects',
      'reports'
    ])
  );

  return (
    <ExampleTree
      aria-label="Example tree with controlled expanded items"
      expandedKeys={expandedKeys}
      onExpandedChange={setExpandedKeys}    />
  );
}

Selection#


By default, TreeView doesn't allow item selection but this can be enabled using the selectionMode prop. Use defaultSelectedKeys to provide a default set of selected items. Note that the value of the selected keys must match the id prop of the TreeViewItem.

The example below enables multiple selection mode, and uses defaultSelectedKeys to select the items with keys "document-a" and "document-b".

<ExampleTree
  aria-label="Example tree with selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="multiple"
  defaultSelectedKeys={['document-a', 'document-b']}/>
<ExampleTree
  aria-label="Example tree with selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="multiple"
  defaultSelectedKeys={['document-a', 'document-b']}/>
<ExampleTree
  aria-label="Example tree with selection"
  defaultExpandedKeys={[
    'projects',
    'project-2'
  ]}
  selectionMode="multiple"
  defaultSelectedKeys={[
    'document-a',
    'document-b'
  ]}/>

Controlled selection#

To programmatically control item selection, use the selectedKeys prop paired with the onSelectionChange callback. The key prop from the selected items will be passed into the callback when the item is pressed, allowing you to update state accordingly.

Here is how you would control selection for the above example.

import type {Selection} from '@adobe/react-spectrum';

function ControlledSelection() {
  let [selectedKeys, setSelectedKeys] = React.useState<Selection>(
    new Set(['document-a', 'document-b'])
  );

  return (
    <ExampleTree
      aria-label="Example tree with controlled selection"
      defaultExpandedKeys={['projects', 'project-2']}
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}    />
  );
}
import type {Selection} from '@adobe/react-spectrum';

function ControlledSelection() {
  let [selectedKeys, setSelectedKeys] = React.useState<
    Selection
  >(new Set(['document-a', 'document-b']));

  return (
    <ExampleTree
      aria-label="Example tree with controlled selection"
      defaultExpandedKeys={['projects', 'project-2']}
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}    />
  );
}
import type {Selection} from '@adobe/react-spectrum';

function ControlledSelection() {
  let [
    selectedKeys,
    setSelectedKeys
  ] = React.useState<
    Selection
  >(
    new Set([
      'document-a',
      'document-b'
    ])
  );

  return (
    <ExampleTree
      aria-label="Example tree with controlled selection"
      defaultExpandedKeys={[
        'projects',
        'project-2'
      ]}
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}    />
  );
}

Single selection#

To limit users to selecting only a single item at a time, selectionMode can be set to single.

<ExampleTree
  aria-label="Example tree with single selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"/>
<ExampleTree
  aria-label="Example tree with single selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"/>
<ExampleTree
  aria-label="Example tree with single selection"
  defaultExpandedKeys={[
    'projects',
    'project-2'
  ]}
  selectionMode="single"/>

Disallow empty selection#

TreeView also supports a disallowEmptySelection prop which forces the user to have at least one item in the TreeView selected at all times. In this mode, if a single item is selected and the user presses it, it will not be deselected.

<ExampleTree
  aria-label="Example tree with disallowed empty selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"
  defaultSelectedKeys={['document-a']}
  disallowEmptySelection/>
<ExampleTree
  aria-label="Example tree with disallowed empty selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"
  defaultSelectedKeys={['document-a']}
  disallowEmptySelection/>
<ExampleTree
  aria-label="Example tree with disallowed empty selection"
  defaultExpandedKeys={[
    'projects',
    'project-2'
  ]}
  selectionMode="single"
  defaultSelectedKeys={[
    'document-a'
  ]}
  disallowEmptySelection/>

Disabled items#

You can disable specific items by providing an array of keys to TreeView via the disabledKeys prop. This will prevent items from being selectable as shown in the example below.

<ExampleTree
  aria-label="Example tree with disabled items"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"
  disabledKeys={['document-a', 'document-b']}/>
<ExampleTree
  aria-label="Example tree with disabled items"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="single"
  disabledKeys={['document-a', 'document-b']}/>
<ExampleTree
  aria-label="Example tree with disabled items"
  defaultExpandedKeys={[
    'projects',
    'project-2'
  ]}
  selectionMode="single"
  disabledKeys={[
    'document-a',
    'document-b'
  ]}/>

Highlight selection#

By default, TreeView uses the checkbox selection style, which includes a checkbox in each item for selection. When the selectionStyle prop is set to "highlight", the checkboxes are hidden, and the selected items are displayed with a highlighted background instead.

In addition to changing the appearance, the selection behavior also changes depending on the selectionStyle prop. In the default checkbox selection style, clicking, tapping, or pressing the Space or Enter keys toggles selection for the focused item. Using the arrow keys moves focus but does not change selection.

In the highlight selection style, however, clicking a item with the mouse replaces the selection with only that item. Using the arrow keys moves both focus and selection. To select multiple items, modifier keys such as Ctrl, Cmd, and Shift can be used. On touch screen devices, selection always behaves as toggle since modifier keys may not be available.

These selection styles implement the behaviors defined in Aria Practices.

<ExampleTree
  aria-label="Example tree with highlight selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="multiple"
  defaultSelectedKeys={['document-a', 'document-b']}
  selectionStyle="highlight"/>
<ExampleTree
  aria-label="Example tree with highlight selection"
  defaultExpandedKeys={['projects', 'project-2']}
  selectionMode="multiple"
  defaultSelectedKeys={['document-a', 'document-b']}
  selectionStyle="highlight"/>
<ExampleTree
  aria-label="Example tree with highlight selection"
  defaultExpandedKeys={[
    'projects',
    'project-2'
  ]}
  selectionMode="multiple"
  defaultSelectedKeys={[
    'document-a',
    'document-b'
  ]}
  selectionStyle="highlight"/>

Actions#


Item actions#

TreeView supports item actions via the onAction prop, which is useful for functionality such as navigation. When nothing is selected, the TreeView performs actions by default when clicking or tapping a item. Items may be selected using the checkbox, or by long pressing on touch devices. When at least one item is selected, the TreeView is in selection mode, and clicking or tapping a item toggles the selection. Actions may also be triggered via the Enter key, and selection using the Space key.

This behavior is slightly different in the highlight selection style, where single clicking selects the item and actions are performed via double click. Touch and keyboard behaviors are unaffected.

<Flex direction="column" gap="size-300">
  <ExampleTree
    aria-label="Example tree with item actions and checkbox selection"
    defaultExpandedKeys={['projects', 'project-2']}
    selectionMode="multiple"
    onAction={key => alert(`Opening item ${key}...`)}  />
  <ExampleTree
    aria-label="Example tree with item actions and highlight selection"
    defaultExpandedKeys={['projects', 'project-2']}
    selectionMode="multiple"
    selectionStyle="highlight"
    onAction={key => alert(`Opening item ${key}...`)}  />
</Flex>
<Flex direction="column" gap="size-300">
  <ExampleTree
    aria-label="Example tree with item actions and checkbox selection"
    defaultExpandedKeys={['projects', 'project-2']}
    selectionMode="multiple"
    onAction={(key) => alert(`Opening item ${key}...`)}  />
  <ExampleTree
    aria-label="Example tree with item actions and highlight selection"
    defaultExpandedKeys={['projects', 'project-2']}
    selectionMode="multiple"
    selectionStyle="highlight"
    onAction={(key) => alert(`Opening item ${key}...`)}  />
</Flex>
<Flex
  direction="column"
  gap="size-300"
>
  <ExampleTree
    aria-label="Example tree with item actions and checkbox selection"
    defaultExpandedKeys={[
      'projects',
      'project-2'
    ]}
    selectionMode="multiple"
    onAction={(key) =>
      alert(
        `Opening item ${key}...`
      )}  />
  <ExampleTree
    aria-label="Example tree with item actions and highlight selection"
    defaultExpandedKeys={[
      'projects',
      'project-2'
    ]}
    selectionMode="multiple"
    selectionStyle="highlight"
    onAction={(key) =>
      alert(
        `Opening item ${key}...`
      )}  />
</Flex>

Tree items may also be links to another page or website. This can be achieved by passing the href prop to the <TreeViewItem> component. Links behave the same way as described above for item actions depending on the selectionMode and selectionStyle.

<TreeView
  aria-label="Example tree with links"
  defaultExpandedKeys={new Set(['bookmarks'])}
  height="size-2000"
  maxWidth="size-6000"
>
  <TreeViewItem id="bookmarks" textValue="Bookmarks">
    <Text>Bookmarks</Text>
    <Folder />
    <TreeViewItem
      href="https://adobe.com/"
      target="_blank"
      id="adobe"
      textValue="Adobe"
    >
      <Text>Adobe</Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://google.com/"
      target="_blank"
      id="google"
      textValue="Google"
    >
      <Text>Google</Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://nytimes.com/"
      target="_blank"
      id="nytimes"
      textValue="New York Times"
    >
      <Text>New York Times</Text>
      <GlobeOutline />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>
<TreeView
  aria-label="Example tree with links"
  defaultExpandedKeys={new Set(['bookmarks'])}
  height="size-2000"
  maxWidth="size-6000"
>
  <TreeViewItem id="bookmarks" textValue="Bookmarks">
    <Text>Bookmarks</Text>
    <Folder />
    <TreeViewItem
      href="https://adobe.com/"
      target="_blank"
      id="adobe"
      textValue="Adobe"
    >
      <Text>Adobe</Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://google.com/"
      target="_blank"
      id="google"
      textValue="Google"
    >
      <Text>Google</Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://nytimes.com/"
      target="_blank"
      id="nytimes"
      textValue="New York Times"
    >
      <Text>New York Times</Text>
      <GlobeOutline />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>
<TreeView
  aria-label="Example tree with links"
  defaultExpandedKeys={new Set(
    ['bookmarks']
  )}
  height="size-2000"
  maxWidth="size-6000"
>
  <TreeViewItem
    id="bookmarks"
    textValue="Bookmarks"
  >
    <Text>
      Bookmarks
    </Text>
    <Folder />
    <TreeViewItem
      href="https://adobe.com/"
      target="_blank"
      id="adobe"
      textValue="Adobe"
    >
      <Text>
        Adobe
      </Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://google.com/"
      target="_blank"
      id="google"
      textValue="Google"
    >
      <Text>
        Google
      </Text>
      <GlobeOutline />
    </TreeViewItem>
    <TreeViewItem
      href="https://nytimes.com/"
      target="_blank"
      id="nytimes"
      textValue="New York Times"
    >
      <Text>
        New York Times
      </Text>
      <GlobeOutline />
    </TreeViewItem>
  </TreeViewItem>
</TreeView>

Client side routing#

The <TreeViewItem> component works with frameworks and client side routers like Next.js and React Router. As with other React Spectrum components that support links, this works via the Provider component at the root of your app. See the client side routing guide to learn how to set this up.

With action groups#

<TreeView
  aria-label="Example tree with action groups"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem childItems={item.childItems} textValue={item.name}>
      <Text>{item.name}</Text>
      {item.icon}
      <ActionGroup
        onAction={(key) => alert(`Item: ${item.id}, Action: ${key}`)}
      >
        <Item key="edit" textValue="Edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="delete" textValue="Delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionGroup>
    </TreeViewItem>
  )}
</TreeView>
<TreeView
  aria-label="Example tree with action groups"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem
      childItems={item.childItems}
      textValue={item.name}
    >
      <Text>{item.name}</Text>
      {item.icon}
      <ActionGroup
        onAction={(key) =>
          alert(`Item: ${item.id}, Action: ${key}`)}
      >
        <Item key="edit" textValue="Edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="delete" textValue="Delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionGroup>
    </TreeViewItem>
  )}
</TreeView>
<TreeView
  aria-label="Example tree with action groups"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem
      childItems={item
        .childItems}
      textValue={item
        .name}
    >
      <Text>
        {item.name}
      </Text>
      {item.icon}
      <ActionGroup
        onAction={(
          key
        ) =>
          alert(
            `Item: ${item.id}, Action: ${key}`
          )}
      >
        <Item
          key="edit"
          textValue="Edit"
        >
          <Edit />
          <Text>
            Edit
          </Text>
        </Item>
        <Item
          key="delete"
          textValue="Delete"
        >
          <Delete />
          <Text>
            Delete
          </Text>
        </Item>
      </ActionGroup>
    </TreeViewItem>
  )}
</TreeView>

With action menus#

<TreeView
  aria-label="Example tree with action menus"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem childItems={item.childItems} textValue={item.name}>
      <Text>{item.name}</Text>
      {item.icon}
      <ActionMenu
        onAction={(key) => alert(`Item: ${item.id}, Action: ${key}`)}
      >
        <Item key="edit" textValue="Edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="delete" textValue="Delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionMenu>
    </TreeViewItem>
  )}
</TreeView>
<TreeView
  aria-label="Example tree with action menus"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem
      childItems={item.childItems}
      textValue={item.name}
    >
      <Text>{item.name}</Text>
      {item.icon}
      <ActionMenu
        onAction={(key) =>
          alert(`Item: ${item.id}, Action: ${key}`)}
      >
        <Item key="edit" textValue="Edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="delete" textValue="Delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionMenu>
    </TreeViewItem>
  )}
</TreeView>
<TreeView
  aria-label="Example tree with action menus"
  height="size-3000"
  maxWidth="size-6000"
  items={items}
>
  {(item: MyItem) => (
    <TreeViewItem
      childItems={item
        .childItems}
      textValue={item
        .name}
    >
      <Text>
        {item.name}
      </Text>
      {item.icon}
      <ActionMenu
        onAction={(
          key
        ) =>
          alert(
            `Item: ${item.id}, Action: ${key}`
          )}
      >
        <Item
          key="edit"
          textValue="Edit"
        >
          <Edit />
          <Text>
            Edit
          </Text>
        </Item>
        <Item
          key="delete"
          textValue="Delete"
        >
          <Delete />
          <Text>
            Delete
          </Text>
        </Item>
      </ActionMenu>
    </TreeViewItem>
  )}
</TreeView>

Props#


TreeView props#

NameTypeDescription
renderEmptyState() => JSX.ElementProvides content to display when there are no items in the tree.
childrenReactNode( (item: object )) => ReactNodeThe contents of the tree.
disabledBehaviorDisabledBehaviorWhether disabledKeys applies to all interactions, or only selection.
itemsIterable<T>Item objects in the collection.
disabledKeysIterable<Key>The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
selectionModeSelectionModeThe type of selection that is allowed in the collection.
disallowEmptySelectionbooleanWhether the collection allows empty selection.
selectedKeys'all'Iterable<Key>The currently selected keys in the collection (controlled).
defaultSelectedKeys'all'Iterable<Key>The initial selected keys in the collection (uncontrolled).
selectionStyle'checkbox''highlight'How selection should be displayed.
expandedKeysIterable<Key>The currently expanded keys in the collection (controlled).
defaultExpandedKeysIterable<Key>The initial expanded keys in the collection (uncontrolled).
Events
NameTypeDescription
onAction( (key: Key )) => void

Handler that is called when a user performs an action on an item. The exact user event depends on the collection's selectionStyle prop and the interaction modality.

onSelectionChange( (keys: Selection )) => voidHandler that is called when the selection changes.
onExpandedChange( (keys: Set<Key> )) => anyHandler that is called when items are expanded or collapsed.
Layout
NameTypeDescription
flexResponsive<stringnumberboolean>When used in a flex layout, specifies how the element will grow or shrink to fit the space available. See MDN.
flexGrowResponsive<number>When used in a flex layout, specifies how the element will grow to fit the space available. See MDN.
flexShrinkResponsive<number>When used in a flex layout, specifies how the element will shrink to fit the space available. See MDN.
flexBasisResponsive<numberstring>When used in a flex layout, specifies the initial main size of the element. See MDN.
alignSelfResponsive<'auto''normal''start''end''center''flex-start''flex-end''self-start''self-end''stretch'>Overrides the alignItems property of a flex or grid container. See MDN.
justifySelfResponsive<'auto''normal''start''end''flex-start''flex-end''self-start''self-end''center''left''right''stretch'>Specifies how the element is justified inside a flex or grid container. See MDN.
orderResponsive<number>The layout order for the element within a flex or grid container. See MDN.
gridAreaResponsive<string>When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. See MDN.
gridColumnResponsive<string>When used in a grid layout, specifies the column the element should be placed in within the grid. See MDN.
gridRowResponsive<string>When used in a grid layout, specifies the row the element should be placed in within the grid. See MDN.
gridColumnStartResponsive<string>When used in a grid layout, specifies the starting column to span within the grid. See MDN.
gridColumnEndResponsive<string>When used in a grid layout, specifies the ending column to span within the grid. See MDN.
gridRowStartResponsive<string>When used in a grid layout, specifies the starting row to span within the grid. See MDN.
gridRowEndResponsive<string>When used in a grid layout, specifies the ending row to span within the grid. See MDN.
Spacing
NameTypeDescription
marginResponsive<DimensionValue>The margin for all four sides of the element. See MDN.
marginTopResponsive<DimensionValue>The margin for the top side of the element. See MDN.
marginBottomResponsive<DimensionValue>The margin for the bottom side of the element. See MDN.
marginStartResponsive<DimensionValue>The margin for the logical start side of the element, depending on layout direction. See MDN.
marginEndResponsive<DimensionValue>The margin for the logical end side of an element, depending on layout direction. See MDN.
marginXResponsive<DimensionValue>The margin for both the left and right sides of the element. See MDN.
marginYResponsive<DimensionValue>The margin for both the top and bottom sides of the element. See MDN.
Sizing
NameTypeDescription
widthResponsive<DimensionValue>The width of the element. See MDN.
minWidthResponsive<DimensionValue>The minimum width of the element. See MDN.
maxWidthResponsive<DimensionValue>The maximum width of the element. See MDN.
heightResponsive<DimensionValue>The height of the element. See MDN.
minHeightResponsive<DimensionValue>The minimum height of the element. See MDN.
maxHeightResponsive<DimensionValue>The maximum height of the element. See MDN.
Positioning
NameTypeDescription
positionResponsive<'static''relative''absolute''fixed''sticky'>Specifies how the element is positioned. See MDN.
topResponsive<DimensionValue>The top position for the element. See MDN.
bottomResponsive<DimensionValue>The bottom position for the element. See MDN.
leftResponsive<DimensionValue>The left position for the element. See MDN. Consider using start instead for RTL support.
rightResponsive<DimensionValue>The right position for the element. See MDN. Consider using start instead for RTL support.
startResponsive<DimensionValue>The logical start position for the element, depending on layout direction. See MDN.
endResponsive<DimensionValue>The logical end position for the element, depending on layout direction. See MDN.
zIndexResponsive<number>The stacking order for the element. See MDN.
isHiddenResponsive<boolean>Hides the element.
Accessibility
NameTypeDescription
idstringThe element's unique identifier. See MDN.
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.
Advanced
NameTypeDescription
UNSAFE_classNamestringSets the CSS className for the element. Only use as a last resort. Use style props instead.
UNSAFE_styleCSSPropertiesSets inline style for the element. Only use as a last resort. Use style props instead.

TreeViewItem props#

NameTypeDescription
childrenReactNodeRendered contents of the tree item or child items.
textValuestringA string representation of the tree item's contents, used for features like typeahead.
hasChildItemsbooleanWhether this item has children, even if not loaded yet.
childItemsIterable<object>A list of child tree item objects used when dynamically rendering the tree item children.
idKeyThe unique id of the tree row.
hrefHrefA URL to link to. See MDN.
hrefLangstringHints at the human language of the linked URL. SeeMDN.
targetHTMLAttributeAnchorTargetThe target window for the link. See MDN.
relstringThe relationship between the linked resource and the current page. See MDN.
downloadbooleanstringCauses the browser to download the linked URL. A string may be provided to suggest a file name. See MDN.
pingstringA space-separated list of URLs to ping when the link is followed. See MDN.
referrerPolicyHTMLAttributeReferrerPolicyHow much of the referrer to send when following the link. See MDN.
routerOptionsRouterOptionsOptions for the configured client side router.
Accessibility
NameTypeDescription
aria-labelstringAn accessibility label for this tree item.

Visual options#


Empty state#

Use the renderEmptyState prop to customize what the TreeView will display if there are no items provided.

import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>No results</Heading>
      <Content>No results found</Content>
    </IllustratedMessage>
  );
}

<TreeView
  aria-label="Example tree for empty state"
  height="size-2400"
  maxWidth="size-6000"
  renderEmptyState={renderEmptyState}
>
  {[]}
</TreeView>
import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>No results</Heading>
      <Content>No results found</Content>
    </IllustratedMessage>
  );
}

<TreeView
  aria-label="Example tree for empty state"
  height="size-2400"
  maxWidth="size-6000"
  renderEmptyState={renderEmptyState}
>
  {[]}
</TreeView>
import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>
        No results
      </Heading>
      <Content>
        No results found
      </Content>
    </IllustratedMessage>
  );
}

<TreeView
  aria-label="Example tree for empty state"
  height="size-2400"
  maxWidth="size-6000"
  renderEmptyState={renderEmptyState}
>
  {[]}
</TreeView>

Testing#


The TreeView features long press interactions on its items depending on the item actions provided and if user is interacting with the list on a touch device. Please see the following sections in the testing docs for more information on how to handle these behaviors in your test suite.

Timers

Desktop vs Mobile

Long press

Please also refer to React Spectrum's test suite if you find that the above isn't sufficient when resolving issues in your own test cases.