esc
navigate select
Browse documentation

Search

Filter to matching nodes while keeping the path to reach them visible.

TreeKit doesn't ship a search input — you already have opinions about how that should look. Instead, it accepts a searchValue string and does the hard part: filtering to matches, keeping every ancestor needed to reach a match visible, and (optionally) auto-expanding those ancestors.

Show code
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<TreeKit data={data} searchValue={search} />
PropTypeDefaultDescription
searchValuestringThe active search term. An empty string (default) disables filtering entirely.
filterFn(node: TreeNode, value: string) => booleancase-insensitive label matchOverride how a node is tested against searchValue.
highlightMatchesbooleantrueWrap the matched substring in a <mark> in the default renderer.
autoExpandOnSearchbooleantrueAutomatically reveal ancestors of a match, independent of expandedIds.

Custom matching

The default matcher checks node.label case-insensitively. Provide filterFn to match against your own data field instead — useful for matching a file path, a tag list, or anything not shown in the label:

<TreeKit
  data={fileTree}
  searchValue={search}
  filterFn={(node, value) =>
    node.data?.path.toLowerCase().includes(value.toLowerCase())
  }
/>

Ancestors stay visible even when they don't match

Search never hides the path to a match, even if an ancestor's own label doesn't contain the search term — "Engineering › Frontend › React" all stay visible when you search "react", even though "Engineering" and "Frontend" don't match the term themselves.

Building it into your own layout

Because searchValue is just a prop, the input can live anywhere — a page header, a sidebar, a command palette. TreeKit re-filters on every render, so debounce the input yourself if you're filtering a very large tree on every keystroke (see Large Trees).