BreadcrumbTree

A breadcrumb-style picker that opens a Finder-style Miller-column panel, anchored to the breadcrumb. The panel floats above any clipping ancestor. Children are fetched on demand with pagination, results are reused when you revisit a node, and the columns auto-scroll to the deepest level.

Demo

Click the chevron to open the panel. Drill into nodes by clicking rows. Click a breadcrumb item to navigate back (the panel closes).

rootPath and fetchChildren

When rootPath is provided (e.g. "my-project"), the component prepends it to every fetchChildren call:

Column depthpath argument to fetchChildren
Root"my-project"
After selecting "datasets""my-project/datasets"
After selecting "droid-2024""my-project/datasets/droid-2024"

Without rootPath, the root call uses "" (empty string) and sub-calls use "datasets", "datasets/droid-2024", etc.

Results are reused when you drill into the same node twice, so revisiting a folder does not trigger a second request.

Pagination

Each column loads its first page on open. Scrolling to the bottom of a column loads the next page automatically.

Floating panel

The panel floats above the content, so it is not clipped by overflow: hidden, transform, or sticky containers. It tracks the trigger as the page scrolls or resizes.

Controlled pathBreadcrumbTree is fully controlled. Row clicks call onNavigate(node, newPath); the caller updates path. The panel stays open after a row click and closes when a breadcrumb item is clicked.

Props

PropTypeDefaultDescription
pathBreadcrumbNode[]Controlled path from root to the selected node. Empty array = root.
onNavigate(node, newPath) => voidFires on row click (panel stays open) and breadcrumb click (panel closes). Update path here.
fetchChildren(path, page, limit) => Promise<FetchChildrenResult>Async loader. path is slash-separated. Prepended with rootPath when set.
rootPathstringRoot prefix shown as the first breadcrumb item.
renderEmpty(parentNode) => ReactNode"No items"Custom empty-state per column. Receives the parent node (null at root).
refreshKeynumber0Increment to re-fetch the deepest visible column.
refreshTokennumber0Increment to drop the whole cache after a host mutation (delete/rename). Open columns re-fetch immediately; otherwise they re-fetch fresh on next open.
placeholderstring"Select folder"Shown when path is empty and rootPath is unset.
classNamestringExtra classes on the wrapper.

Types

ts
interface BreadcrumbNode {
  id: string
  name: string
  /** When false, hides the chevron — signals no sub-children exist. */
  hasChildren?: boolean
}

interface FetchChildrenResult {
  items: BreadcrumbNode[]
  totalPages: number
}

// Per-column state, also exported for callers that inspect column status.
interface ColumnData {
  items: BreadcrumbNode[]
  page: number
  totalPages: number
  hasMore: boolean
  loading: boolean
  error: Error | null
}