Skip to content

Tree view

Tree view is a hierarchical list of items that may have a parent-child relationship where children can be toggled into view by expanding or collapsing their parent item.

Anatomy

  1. Parent node: A node that hides and shows child nodes by expanding and collapsing
  2. End node: A node with no children
  3. Nesting level indicator lines: Each line represents a level of nesting depth
  4. Chevron: Toggles a parent node expanded or collapsed and indicates expanded/collapsed state
  5. Leading visual (optional): A visual cue for additional context
  6. Node label: The text label for the node
  7. Trailing visual (optional): Same as a leading visual, but at the end

Content

Node label

Node labels should be a succinct title for whatever entity the node represents. For example, a file name.

By default, labels are truncated to a single line. However, node labels may be wrapped to multiple lines in cases where the entire label must always be visible.

When a node's text is truncated, it should still be accessible to users via a browser-native tooltip.

Leading and trailing visuals

Leading and trailing visuals may be used as visual cues that communicate more information about a node.

If the leading or trailing visual contains useful information, make sure it has a screen-reader accessible text label. Note that the file and folder icons are not considered useful information, as the TreeView's underlying markup will communicate their presence.

Either all nodes have leading visuals, or no nodes have leading visuals. Leading visuals must be accompanied by a text label.

Do
3 1 DO-consistentLeadingViz
Don’t
3 2 DONT-mixLeadingViz

Interaction

Keyboard users can expand or collapse nodes without activating them by using the left and right arrow keys.

To give cursor users an equivalent experience, there are two click areas:

  • clicking the chevron only expands or collapses the node
  • clicking anywhere else will activate the node

If the node cannot be activated, clicking anywhere on the node will expand it.

Nodes may not contain any other interactive elements besides the chevron. Activating a node can only perform one action. For example, following a link.

Do
5 1 DO-singleActionPerNode

Limit nodes to a single action

Don’t
5 2 DONT-multiActionsPerNode

Don't put other interactive elements in a node

We have investigated the feasibility of supporting a context menu for each node, and that could be something we support in the future if we find we need it.

States

6 nodeStates

Nodes have different visual treatments to indicate their state and hint what kind of user input they're ready to receive. These styles are inherited from the action list component.

Nesting

Nodes are visually nested using whitespace and alignment.

Align leading icons

Icons at the same level are center-aligned. The chevron should not break the alignment: parent nodes and child nodes keep their leading visuals aligned.
19 leadingVizAlignment

Indentation

Each level of nesting increments the size of the indentation.
20 nodesWithIndentations

Nesting level indicator lines

On devices with :hover support, the nesting indicator lines fade in when the user mouses over the entire component, or when there's focus inside the component.

On devices without :hover, the nesting indicator lines appear at all times.

21 1 showLinesOnHover21 2 alwaysShowLinesOnTouch

Handling deeply nested nodes

Navigating through deeply nested nodes can be cumbersome and visually clunky. If accessing nodes deeper than 10 levels deep is a common interaction for your use-case, reconsider whether a tree view is the best pattern.

Tree views are designed to be horizontally compact in order to support many levels of nesting and long node labels. This makes content truncation and horizontal overflow less likely to occur for deeply nested nodes, but infinite nesting makes those cases impossible to prevent entirely.

There are some strategies to avoid a tree view that horizontally overflows it's container:

  • Allow the tree view's container to be manually resized
  • Once a certain depth is reached, start the tree view from a deeper parent node and provide a way to navigate back up the tree
  • Remove leading and trailing visuals

If a tree view is truly the best pattern for your use-case and none of the suggested strategies prevent horizontal overflow, opt for horizontal scrolling as a last resort instead of hiding overflowing content.

Usage

Tree views are only for hierarchical lists

A tree view solves a very specific problem. It's not a multi-purpose tool like an action list.

Before reaching for a tree view, first make sure that:
  • the items in the tree view represent a list
  • the list items are likely to have a parent-child relationship with more than 1 level of nesting
  • the expand and collapse behavior aids in navigation instead of just making it more complex
Some good cases for a tree view are:
  • navigating the file structure of a repo
  • navigating a codebase's symbols (types, constants, functions, etc) organized by their scope hierarchy
A tree view would not be appropriate for:
  • global sidebar navigation
  • an FAQ that collapses answers under question headings
Do
7 1 DO-useForList

Use a tree view for a list

Don’t
7 2 DONT-useForAccordion

Don't use a tree view for expanding and collapsing sections of content

Expanding and collapsing nodes

A tree view can be a frustrating pattern if it forces users to spend a lot of time expanding and collapsing nodes to find what they're looking for.

Expanded by default

When a child node is active, all of it's parent nodes should be expanded.

If it's likely that a user will want to interact with all or most of the nodes in the tree, render the tree with all parent nodes expanded to start. For example: when reviewing a pull request, all of the changed files are shown to make it easy for reviewers to scan and navigate the changed files.

8 openActiveParentNodes

Preserve expanded state when parent node is collapsed

If a user expands nested parent nodes and then collapses a parent node higher in the hierarchy, persist the expanded parent nodes lower in the hierarchy. It could be frustrating to have to re-expand the nodes that were already opened.

Optionally combine parent nodes

Asynchronously loading child nodes

Show placeholder nodes (preferred)

General loading indicator (fallback)

Handling loading errors

Inform users why the data cannot be retrieved and give them a path to resolve it. The error message cannot appear in the tree view because it is not semantically a node in the tree. Instead, the error message should appear in a dialog with an optional call-to-action that can resolve the error.

If the user dismisses the dialog, focus should be moved back to the collapsed parent node. If the user clicks a call-to-action that attempts to load the child nodes again, focus should be moved to the node in the tree that communicates the child nodes are loading.

If we don't have enough information to write a useful error message, it's ok to write something generic like "Couldn't load".

13 errorHandling
-

Composition

Examples

Provide sufficient vertical space

Be considerate of the amount of vertical space a tree view can take up when all of it's nodes are expanded.

Do
18 1 DO-renderWithEnoughSpace

Render a tree view in an area with enough vertical space to comfortably browse the list, and doesn't push important content or controls below the bottom of the viewport

Don’t
18 2 DONT-renderTooSmall

Don't render a tree view in an area that pushes down other content or forces users to scroll through small segments of the list

Accessibility

Label

Tree views need an accessible name to be supplied, ideally via `aria-labelledby` pointing at an appropriate heading before the tree. If the tree view has enough surrounding context that it doesn't need a visible label, apply `sr-only` to the heading to hide it visually, but preserve the underlying programatic association.

25 labelledTreeView

Keyboard navigation

Key(s)Description
EnterPerforms the default action (e.g. onclick event) for the focused node which is to activate the link.
TabMoves focus outside of the tree view to the next focusable node.
ArrowDown
  • Moves focus to the next node that is focusable without opening or closing a node.
  • If focus is on the last node, does nothing.
ArrowUp
  • Moves focus to the previous node that is focusable without opening or closing a node.
  • If focus is on the first node, does nothing.
ArrowRight
  • When focus is on a closed node, opens the node; focus does not move.
  • When focus is on a open node, moves focus to the first child node.
  • When focus is on an end node, does nothing.
ArrowLeft
  • 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, all printable characters
  • 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.
  • Focus should not move if no nodes match the search.

Typeahead behavior

Focus moves to the next node with a name that starts with the typed character(s). Wait for 300ms from the last key press to stop searching.

Typeahead behavior is case insensitive, and any printable character can be used, not just alphanumeric characters.

The `Space` key is ignored because it's being reserved for toggling a tree view node's checkbox. Nodes with checkboxes are not supported yet, but they may in the future.

Focus behavior

Focus in

If moving focus into the TreeView for the first time, focus on the first node.

If moving focus back into the TreeView after the user already interacted with it, focus the previously focused TreeView node.

Focus out

If activating a node causes new content to appear without a page refresh, focus should be moved to the new content. By default, focus should be moved to the first focusable control within the main content region. We should avoid skipping any content so that the user can easily go back to the TreeView if they activate a node by accident.

If the first focusable element would cause a confusing experience for folks who listen to content using a screen reader, then an alternate element may be defined

Minimum click target area for the chevron

The chevron has a generous click area to make it an easier target to hit, but it's kept compact to preserve horizontal space in deeply nested nodes.

When we detect that the user is on a device with a coarse pointer, the click target is enlarged.

24 minTouchTarget