Skip to main content
Since Shoelace 2.0 Code stable

Tree

<sl-tree> | SlTree

Trees allow you to display a hierarchical list of selectable tree items. Items with children can be expanded and collapsed as desired by the user.

Examples

Basic Tree

Deciduous Birch Maple Field maple Red maple Sugar maple Oak Coniferous Cedar Pine Spruce Non-trees Bamboo Cactus Fern
<sl-tree>
  <sl-tree-item>
    Deciduous
    <sl-tree-item>Birch</sl-tree-item>
    <sl-tree-item>
      Maple
      <sl-tree-item>Field maple</sl-tree-item>
      <sl-tree-item>Red maple</sl-tree-item>
      <sl-tree-item>Sugar maple</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>Oak</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Coniferous
    <sl-tree-item>Cedar</sl-tree-item>
    <sl-tree-item>Pine</sl-tree-item>
    <sl-tree-item>Spruce</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Non-trees
    <sl-tree-item>Bamboo</sl-tree-item>
    <sl-tree-item>Cactus</sl-tree-item>
    <sl-tree-item>Fern</sl-tree-item>
  </sl-tree-item>
</sl-tree>
sl-tree
  sl-tree-item
    | Deciduous
    sl-tree-item Birch
    sl-tree-item
      | Maple
      sl-tree-item Field maple
      sl-tree-item Red maple
      sl-tree-item Sugar maple
    sl-tree-item Oak
  sl-tree-item
    | Coniferous
    sl-tree-item Cedar
    sl-tree-item Pine
    sl-tree-item Spruce
  sl-tree-item
    | Non-trees
    sl-tree-item Bamboo
    sl-tree-item Cactus
    sl-tree-item Fern
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => (
  <SlTree>
    <SlTreeItem>
      Deciduous
      <SlTreeItem>Birch</SlTreeItem>
      <SlTreeItem>
        Maple
        <SlTreeItem>Field maple</SlTreeItem>
        <SlTreeItem>Red maple</SlTreeItem>
        <SlTreeItem>Sugar maple</SlTreeItem>
      </SlTreeItem>
      <SlTreeItem>Oak</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Coniferous
      <SlTreeItem>Cedar</SlTreeItem>
      <SlTreeItem>Pine</SlTreeItem>
      <SlTreeItem>Spruce</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Non-trees
      <SlTreeItem>Bamboo</SlTreeItem>
      <SlTreeItem>Cactus</SlTreeItem>
      <SlTreeItem>Fern</SlTreeItem>
    </SlTreeItem>
  </SlTree>
);

Selection Modes

The selection attribute lets you change the selection behavior of the tree.

  • Use single to allow the selection of a single item (default).
  • Use multiple to allow the selection of multiple items.
  • Use leaf to only allow leaf nodes to be selected.
Single Multiple Leaf
Item 1 Item A Item Z Item Y Item X Item B Item C Item 2 Item 3
<sl-select id="selection-mode" value="single" label="Selection">
  <sl-option value="single">Single</sl-option>
  <sl-option value="multiple">Multiple</sl-option>
  <sl-option value="leaf">Leaf</sl-option>
</sl-select>

<br />

<sl-tree class="tree-selectable">
  <sl-tree-item>
    Item 1
    <sl-tree-item>
      Item A
      <sl-tree-item>Item Z</sl-tree-item>
      <sl-tree-item>Item Y</sl-tree-item>
      <sl-tree-item>Item X</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>Item B</sl-tree-item>
    <sl-tree-item>Item C</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>Item 2</sl-tree-item>
  <sl-tree-item>Item 3</sl-tree-item>
</sl-tree>

<script>
  const selectionMode = document.querySelector('#selection-mode');
  const tree = document.querySelector('.tree-selectable');

  selectionMode.addEventListener('sl-change', () => {
    tree.querySelectorAll('sl-tree-item').forEach(item => (item.selected = false));
    tree.selection = selectionMode.value;
  });
</script>
sl-select#selection-mode value="single" label="Selection"
  sl-option value="single" Single
  sl-option value="multiple" Multiple
  sl-option value="leaf" Leaf
br
sl-tree.tree-selectable
  sl-tree-item
    | Item 1
    sl-tree-item
      | Item A
      sl-tree-item Item Z
      sl-tree-item Item Y
      sl-tree-item Item X
    sl-tree-item Item B
    sl-tree-item Item C
  sl-tree-item Item 2
  sl-tree-item Item 3

javascript:
  const selectionMode = document.querySelector(#selection-mode);
  const tree = document.querySelector(.tree-selectable);

  selectionMode.addEventListener(sl-change, () => {
    tree.querySelectorAll(sl-tree-item).forEach(item => (item.selected = false));
    tree.selection = selectionMode.value;
  });
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => {
  const [selection, setSelection] = useState('single');

  return (
    <>
      <SlSelect label="Selection" value={selection} onSlChange={event => setSelection(event.target.value)}>
        <SlMenuItem value="single">single</SlMenuItem>
        <SlMenuItem value="multiple">multiple</SlMenuItem>
        <SlMenuItem value="leaf">leaf</SlMenuItem>
      </SlSelect>

      <br />

      <SlTree selection={selection}>
        <SlTreeItem>
          Item 1
          <SlTreeItem>
            Item A
            <SlTreeItem>Item Z</SlTreeItem>
            <SlTreeItem>Item Y</SlTreeItem>
            <SlTreeItem>Item X</SlTreeItem>
          </SlTreeItem>
          <SlTreeItem>Item B</SlTreeItem>
          <SlTreeItem>Item C</SlTreeItem>
        </SlTreeItem>
        <SlTreeItem>Item 2</SlTreeItem>
        <SlTreeItem>Item 3</SlTreeItem>
      </SlTree>
    </>
  );
};

Showing Indent Guides

Indent guides can be drawn by setting --indent-guide-width. You can also change the color, offset, and style, using --indent-guide-color, --indent-guide-style, and --indent-guide-offset, respectively.

Deciduous Birch Maple Field maple Red maple Sugar maple Oak Coniferous Cedar Pine Spruce Non-trees Bamboo Cactus Fern
<sl-tree class="tree-with-lines">
  <sl-tree-item expanded>
    Deciduous
    <sl-tree-item>Birch</sl-tree-item>
    <sl-tree-item expanded>
      Maple
      <sl-tree-item>Field maple</sl-tree-item>
      <sl-tree-item>Red maple</sl-tree-item>
      <sl-tree-item>Sugar maple</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>Oak</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Coniferous
    <sl-tree-item>Cedar</sl-tree-item>
    <sl-tree-item>Pine</sl-tree-item>
    <sl-tree-item>Spruce</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Non-trees
    <sl-tree-item>Bamboo</sl-tree-item>
    <sl-tree-item>Cactus</sl-tree-item>
    <sl-tree-item>Fern</sl-tree-item>
  </sl-tree-item>
</sl-tree>

<style>
  .tree-with-lines {
    --indent-guide-width: 1px;
  }
</style>
sl-tree.tree-with-lines
  sl-tree-item expanded="true"
    | Deciduous
    sl-tree-item Birch
    sl-tree-item expanded="true"
      | Maple
      sl-tree-item Field maple
      sl-tree-item Red maple
      sl-tree-item Sugar maple
    sl-tree-item Oak
  sl-tree-item
    | Coniferous
    sl-tree-item Cedar
    sl-tree-item Pine
    sl-tree-item Spruce
  sl-tree-item
    | Non-trees
    sl-tree-item Bamboo
    sl-tree-item Cactus
    sl-tree-item Fern

css:
  .tree-with-lines {
    --indent-guide-width: 1px;
  }
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => (
  <SlTree class="tree-with-lines" style={{ '--indent-guide-width': '1px' }}>
    <SlTreeItem expanded>
      Deciduous
      <SlTreeItem>Birch</SlTreeItem>
      <SlTreeItem expanded>
        Maple
        <SlTreeItem>Field maple</SlTreeItem>
        <SlTreeItem>Red maple</SlTreeItem>
        <SlTreeItem>Sugar maple</SlTreeItem>
      </SlTreeItem>
      <SlTreeItem>Oak</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Coniferous
      <SlTreeItem>Cedar</SlTreeItem>
      <SlTreeItem>Pine</SlTreeItem>
      <SlTreeItem>Spruce</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Non-trees
      <SlTreeItem>Bamboo</SlTreeItem>
      <SlTreeItem>Cactus</SlTreeItem>
      <SlTreeItem>Fern</SlTreeItem>
    </SlTreeItem>
  </SlTree>
);

Lazy Loading

Use the lazy attribute on a tree item to indicate that the content is not yet present and will be loaded later. When the user tries to expand the node, the loading state is set to true and the sl-lazy-load event will be emitted to allow you to load data asynchronously. The item will remain in a loading state until its content is changed.

If you want to disable this behavior after the first load, simply remove the lazy attribute and, on the next expand, the existing content will be shown instead.

Available Trees
<sl-tree>
  <sl-tree-item lazy>Available Trees</sl-tree-item>
</sl-tree>

<script type="module">
  const lazyItem = document.querySelector('sl-tree-item[lazy]');

  lazyItem.addEventListener('sl-lazy-load', () => {
    // Simulate asynchronous loading
    setTimeout(() => {
      const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];

      for (const item of subItems) {
        const treeItem = document.createElement('sl-tree-item');
        treeItem.innerText = item;
        lazyItem.append(treeItem);
      }

      // Disable lazy mode once the content has been loaded
      lazyItem.lazy = false;
    }, 1000);
  });
</script>
sl-tree
  sl-tree-item lazy="true" Available Trees

<script type="module">
  const lazyItem = document.querySelector('sl-tree-item[lazy]');

  lazyItem.addEventListener('sl-lazy-load', () => {
    // Simulate asynchronous loading
    setTimeout(() => {
      const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];

      for (const item of subItems) {
        const treeItem = document.createElement('sl-tree-item');
        treeItem.innerText = item;
        lazyItem.append(treeItem);
      }

      // Disable lazy mode once the content has been loaded
      lazyItem.lazy = false;
    }, 1000);
  });
</script>
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => {
  const [childItems, setChildItems] = useState([]);
  const [lazy, setLazy] = useState(true);

  const handleLazyLoad = () => {
    // Simulate asynchronous loading
    setTimeout(() => {
      setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']);

      // Disable lazy mode once the content has been loaded
      setLazy(false);
    }, 1000);
  };

  return (
    <SlTree>
      <SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
        Available Trees
        {childItems.map(item => (
          <SlTreeItem>{item}</SlTreeItem>
        ))}
      </SlTreeItem>
    </SlTree>
  );
};

Customizing the Expand and Collapse Icons

Use the expand-icon and collapse-icon slots to change the expand and collapse icons, respectively. To disable the animation, override the rotate property on the expand-button part as shown below.

Deciduous Birch Maple Field maple Red maple Sugar maple Oak Coniferous Cedar Pine Spruce Non-trees Bamboo Cactus Fern
<sl-tree class="custom-icons">
  <sl-icon name="plus" slot="expand-icon"></sl-icon>
  <sl-icon name="minus" slot="collapse-icon"></sl-icon>

  <sl-tree-item>
    Deciduous
    <sl-tree-item>Birch</sl-tree-item>
    <sl-tree-item>
      Maple
      <sl-tree-item>Field maple</sl-tree-item>
      <sl-tree-item>Red maple</sl-tree-item>
      <sl-tree-item>Sugar maple</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>Oak</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Coniferous
    <sl-tree-item>Cedar</sl-tree-item>
    <sl-tree-item>Pine</sl-tree-item>
    <sl-tree-item>Spruce</sl-tree-item>
  </sl-tree-item>

  <sl-tree-item>
    Non-trees
    <sl-tree-item>Bamboo</sl-tree-item>
    <sl-tree-item>Cactus</sl-tree-item>
    <sl-tree-item>Fern</sl-tree-item>
  </sl-tree-item>
</sl-tree>

<style>
  .custom-icons sl-tree-item::part(expand-button) {
    /* Disable the expand/collapse animation */
    rotate: none;
  }
</style>
sl-tree.custom-icons
  sl-icon name="plus" slot="expand-icon"
  sl-icon name="minus" slot="collapse-icon"
  sl-tree-item
    | Deciduous
    sl-tree-item Birch
    sl-tree-item
      | Maple
      sl-tree-item Field maple
      sl-tree-item Red maple
      sl-tree-item Sugar maple
    sl-tree-item Oak
  sl-tree-item
    | Coniferous
    sl-tree-item Cedar
    sl-tree-item Pine
    sl-tree-item Spruce
  sl-tree-item
    | Non-trees
    sl-tree-item Bamboo
    sl-tree-item Cactus
    sl-tree-item Fern

css:
  .custom-icons sl-tree-item::part(expand-button) {
    /* Disable the expand/collapse animation */
    rotate: none;
  }
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => (
  <SlTree>
    <SlIcon name="plus" slot="expand-icon"></SlIcon>
    <SlIcon name="minus" slot="collapse-icon"></SlIcon>

    <SlTreeItem>
      Deciduous
      <SlTreeItem>Birch</SlTreeItem>
      <SlTreeItem>
        Maple
        <SlTreeItem>Field maple</SlTreeItem>
        <SlTreeItem>Red maple</SlTreeItem>
        <SlTreeItem>Sugar maple</SlTreeItem>
      </SlTreeItem>
      <SlTreeItem>Oak</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Coniferous
      <SlTreeItem>Cedar</SlTreeItem>
      <SlTreeItem>Pine</SlTreeItem>
      <SlTreeItem>Spruce</SlTreeItem>
    </SlTreeItem>

    <SlTreeItem>
      Non-trees
      <SlTreeItem>Bamboo</SlTreeItem>
      <SlTreeItem>Cactus</SlTreeItem>
      <SlTreeItem>Fern</SlTreeItem>
    </SlTreeItem>
  </SlTree>
);

With Icons

Decorative icons can be used before labels to provide hints for each node.

Documents Photos birds.jpg kitten.jpg puppy.jpg Writing draft.txt final.pdf sales.xls
<sl-tree class="tree-with-icons">
  <sl-tree-item expanded>
    <sl-icon name="folder"></sl-icon>
    Documents

    <sl-tree-item>
      <sl-icon name="folder"> </sl-icon>
      Photos
      <sl-tree-item>
        <sl-icon name="photo"></sl-icon>
        birds.jpg
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="photo"></sl-icon>
        kitten.jpg
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="photo"></sl-icon>
        puppy.jpg
      </sl-tree-item>
    </sl-tree-item>

    <sl-tree-item>
      <sl-icon name="folder"></sl-icon>
      Writing
      <sl-tree-item>
        <sl-icon name="document-text"></sl-icon>
        draft.txt
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="document-arrow-down"></sl-icon>
        final.pdf
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="document-chart-bar"></sl-icon>
        sales.xls
      </sl-tree-item>
    </sl-tree-item>
  </sl-tree-item>
</sl-tree>
sl-tree.tree-with-icons
  sl-tree-item expanded="true"
    sl-icon name="folder"
    | Documents
    sl-tree-item
      sl-icon name="folder"
      | Photos
      sl-tree-item
        sl-icon name="photo"
        | birds.jpg
      sl-tree-item
        sl-icon name="photo"
        | kitten.jpg
      sl-tree-item
        sl-icon name="photo"
        | puppy.jpg
    sl-tree-item
      sl-icon name="folder"
      | Writing
      sl-tree-item
        sl-icon name="document-text"
        | draft.txt
      sl-tree-item
        sl-icon name="document-arrow-down"
        | final.pdf
      sl-tree-item
        sl-icon name="document-chart-bar"
        | sales.xls
import SlIcon from '@teamshares/shoelace/dist/react/icon';
import SlTree from '@teamshares/shoelace/dist/react/tree';
import SlTreeItem from '@teamshares/shoelace/dist/react/tree-item';

const App = () => {
  return (
    <SlTree class="tree-with-icons">
      <SlTreeItem expanded>
        <SlIcon name="folder" />
        Root
        <SlTreeItem>
          <SlIcon name="folder" />
          Folder 1<SlTreeItem>
            <SlIcon name="files" />
            File 1 - 1
          </SlTreeItem>
          <SlTreeItem disabled>
            <SlIcon name="files" />
            File 1 - 2
          </SlTreeItem>
          <SlTreeItem>
            <SlIcon name="files" />
            File 1 - 3
          </SlTreeItem>
        </SlTreeItem>
        <SlTreeItem>
          <SlIcon name="files" />
          Folder 2<SlTreeItem>
            <SlIcon name="files" />
            File 2 - 1
          </SlTreeItem>
          <SlTreeItem>
            <SlIcon name="files" />
            File 2 - 2
          </SlTreeItem>
        </SlTreeItem>
        <SlTreeItem>
          <SlIcon name="files" />
          File 1
        </SlTreeItem>
      </SlTreeItem>
    </SlTree>
  );
};

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Script Import Bundler React

To import this component from the CDN using a script tag:

<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.1.0/cdn/components/tree/tree.js"></script>

To import this component from the CDN using a JavaScript import:

import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.1.0/cdn/components/tree/tree.js';

To import this component using a bundler:

import '@shoelace-style/shoelace/dist/components/tree/tree.js';

To import this component as a React component:

import SlTree from '@shoelace-style/shoelace/dist/react/tree';

Slots

Name Description
(default) The default slot.
expand-icon The icon to show when the tree item is expanded. Works best with <sl-icon>.
collapse-icon The icon to show when the tree item is collapsed. Works best with <sl-icon>.

Learn more about using slots.

Properties

Name Description Reflects Type Default
selection The selection behavior of the tree. Single selection allows only one node to be selected at a time. Multiple displays checkboxes and allows more than one node to be selected. Leaf allows only leaf nodes to be selected. 'single' | 'multiple' | 'leaf' 'single'
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
sl-selection-change onSlSelectionChange Emitted when a tree item is selected or deselected. { selection: SlTreeItem[] }

Learn more about events.

Custom Properties

Name Description Default
--indent-size The size of the indentation for nested items. var(–sl-spacing-medium)
--indent-guide-color The color of the indentation line. var(–sl-color-neutral-200)
--indent-guide-offset The amount of vertical spacing to leave between the top and bottom of the indentation line’s starting position. 0
--indent-guide-style The style of the indentation line, e.g. solid, dotted, dashed. solid
--indent-guide-width The width of the indentation line. 0

Learn more about customizing CSS custom properties.

Parts

Name Description
base The component’s base wrapper.

Learn more about customizing CSS parts.