TreeMenu
import TreeMenu from 'react-simple-tree-menu';| Prop | Type | Default | Description |
|---|---|---|---|
data | TreeNodeObject | TreeNodeInArray[] | required | The tree to render. See Data formats. |
onClickItem | (item: Item) => void | () => {} | Fires on item click with the full Item (including any custom user props you put on nodes). |
initialActiveKey | string | undefined | Initial active/selected key. Read once. |
initialFocusKey | string | undefined | Initial focused key for keyboard. Read once. |
initialOpenNodes | string[] | [] | Keys of nodes that start open. Read once. |
activeKey | string | — | Controlled active key. |
focusKey | string | — | Controlled focus key. |
openNodes | string[] | — | Controlled open-nodes list. |
hasSearch | boolean | true | Whether to pass a search callback to the render-props function. |
debounceTime | number | 125 | Milliseconds between last keystroke and filter. |
locale | LocaleFunction | — | Transform labels before matching/render. See Types. |
matchSearch | MatchSearchFunction | — | Replace the default substring matcher. See Types. |
disableKeyboard | boolean | false | Skip the <div> keyboard wrapper. |
resetOpenNodesOnDataUpdate | boolean | false | Reset open nodes when data identity changes. |
classNames | TreeMenuClassNames | {} | Class names appended to rstm-* anchors. See Theming. |
labels | TreeMenuLabels | {} | Customize the default UI’s i18n strings. |
keySeparator | string | '/' | Delimiter joining node keys into paths. Change if your keys contain / (URLs, paths) — the library uses it everywhere: emitted Item.key, openNodes lookups, LEFT-arrow parent focus, unflatten(). |
children | (props: TreeMenuChildren) => ReactNode | defaultChildren | Render-props function. |
Imperative handle
Section titled “Imperative handle”Attach a ref to reset state programmatically:
import TreeMenu, { type TreeMenuHandle } from 'react-simple-tree-menu';import { useRef } from 'react';
function MyTree() { const ref = useRef<TreeMenuHandle>(null);
return ( <> <button onClick={() => ref.current?.resetOpenNodes()}>Reset</button> <button onClick={() => ref.current?.expandAll()}>Expand all</button> <button onClick={() => ref.current?.collapseAll()}>Collapse all</button> <TreeMenu ref={ref} data={data} /> </> );}| Method | Signature | Notes |
|---|---|---|
resetOpenNodes | (openNodes?, activeKey?, focusKey?) => void | Reset expansion state. Also clears activeKey/focusKey/searchTerm. |
expandAll | () => void | Open every branch. Preserves active/focus/search. No-op under controlled openNodes. |
collapseAll | () => void | Close every branch. Preserves active/focus/search. No-op under controlled openNodes. |
Controlled openNodes users: the imperative methods are no-ops (the parent owns the slot). Use the exported helper instead:
import TreeMenu, { collectBranchKeys } from 'react-simple-tree-menu';
const [open, setOpen] = useState<string[]>([]);<button onClick={() => setOpen(collectBranchKeys(data))}>Expand all</button><TreeMenu data={data} openNodes={open} />collectBranchKeys(data, keySeparator?) is O(N) and microseconds even on 100k-node trees. The real cost after expand-all is rendering every branch’s children — for trees that grow beyond ~2k visible rows, pair with the virtualization guide.
Render-props shape
Section titled “Render-props shape”When you pass a function as children, it receives:
type TreeMenuChildren = { items: Item[]; // Flattened, filtered, search-aware search?: (term: string) => void; // Call to filter (undefined if hasSearch=false) searchTerm?: string; // Current search term resetOpenNodes: ( openNodes?: string[], activeKey?: string, focusKey?: string ) => void;};Item / TreeMenuItem
Section titled “Item / TreeMenuItem”Each entry in items is a TreeMenuItem — an Item (what walk()
emits) decorated with per-render handlers and state flags:
import type { MouseEvent } from 'react';
interface Item { key: string; // Unique key path, e.g. "fruit/apple" label: string; // Display label (after `locale` if provided) hasNodes: boolean; // True if the item has children isOpen: boolean; // True if the item is expanded level: number; // 0-indexed depth posInSet?: number; // 1-based position among siblings (WAI-ARIA) setSize?: number; // Number of siblings (WAI-ARIA) [name: string]: unknown; // Any custom fields from the original node}
interface TreeMenuItem extends Item { active?: boolean; // Matches `activeKey` focused?: boolean; // Matches `focusKey` onClick: (event: MouseEvent<HTMLLIElement>) => void; toggleNode?: () => void; // Toggles open state (branches only)}The default UI fires onClick from a <div>, not a <li> — the type
describes what <ItemComponent> was historically placed on. If you pass
the handler to a different element in a custom render-prop, you may need
an event-type cast.
unflatten
Section titled “unflatten”Helper for custom render-props that reconstructs a nested tree shape
from the flat items array walk() emits. Group-by-parent, zero React.
import { unflatten } from 'react-simple-tree-menu';
function unflatten<T extends { key: string }>( items: readonly T[]): { roots: T[]; childrenByParent: Map<string, T[]> };- Generic over
T extends { key: string }— pass rawItem,TreeMenuItem, or any consumer-projected shape. childrenByParentis aMapfrom each parent’s key to its ordered children (input order preserved).- Same helper
defaultChildrenuses internally, so your custom render-props share the same grouping contract as the default UI.
See the render-props guide for a complete nested-DOM example.