Format follows Keep a Changelog. Versioning follows SemVer.
Full ground-up rewrite. Public API preserved byte-for-byte modulo the
specific removals called out in Breaking changes — consumers who
aren't using cacheSearch and are already on React ≥ 16.14 upgrade
with a single npm install and no code changes.
- React peer floor raised from
>=16.6.3to>=16.14. 16.14 (Oct 2020) is the first version that exposesreact/jsx-runtime, which the new bundler relies on. The 16.6–16.13 slice is a narrow window of React history; any consumer on 16.14+, 17, 18, or 19 is unaffected. cacheSearchprop removed. It was a leaky abstraction tied tofast-memoize's stringified-arg cache, which silently returned stale results whenlocaleormatchSearchchanged. Internal memoization is now plainuseMemokeyed on real dependencies — correct by default, no user-facing knob. Delete the prop from any call site.- Default
onClickItemis now a no-op. v1 defaulted toconsole.log, which was never intended as shipped behavior. Supply your own handler if you need click feedback. - Default search placeholder + aria-label changed from
"Type and search"to"Search". Restore vialabels={{ searchPlaceholder: 'Type and search', searchAriaLabel: 'Type and search' }}. - Default toggle glyphs changed from
"+" / "-"to Unicode triangles"▸" / "▾". Restore viaopenedIcon/closedIconprops onItemComponentin a custom render-prop. - Default palette swapped from saturated blue (
#179ed3) to monochrome black-and-white — black active row on a white background with a mid-grey focus ring, optimized for light backgrounds. Tailwind v4 consumers'--color-primarystill wins for active state when present, so brand colors flow through automatically. Dark-bg consumers override tokens on their own scope (e.g.:root.dark .my-panel { --rstm-text-color: ... }). TreeNodeandTreeNodeInArraytypes tightened.TreeNode(object-form) now forbidskeyviakey?: never, andTreeNodeInArray(array-form) forbidsindexviaindex?: never. Under v1 both fields were silently accepted on the wrong shape via the open index signature. v2 rejects the mismatch at compile time — correctly typed v1 code is unaffected; code that accidentally set both now gets a clear TS error pointing at the one that's ignored.
None of the above changes the public .d.ts export shape for
symbols or props that remained.
- WAI-ARIA tree pattern on rendered DOM:
role="tree"on the container,role="treeitem"on each item, plusaria-expanded,aria-level,aria-selected,aria-setsize,aria-posinset. Roving tabindex (focused itemtabIndex=0, others-1). - Arrow-key navigation matches WAI-ARIA tree pattern.
→on an open branch now moves focus to the first child (previously a no-op). Closed branch / leaf behavior unchanged. Matches VS Code, Finder, GitHub file tree muscle memory. classNamesprop (TreeMenuClassNamestype). Per-slot strings appended to therstm-*anchor classes — Tailwind / utility-class users can skip the default CSS import and style with their framework while the anchor classes remain as inert strings for backward-compat CSS overrides.labelsprop (TreeMenuLabelstype). i18n-friendly overrides for default UI copy:searchPlaceholder,searchAriaLabel.TreeMenuHandleref shape.useImperativeHandleexposesresetOpenNodes, plusexpandAll()andcollapseAll()for one-call expansion state changes.expandAll()does one O(N) walk of the data tree (microseconds even on 100k nodes) to collect every branch key, then dispatches a single state update;collapseAll()is a no-arg reset to zero open branches. Both preserve the user's current active/focus/search state (unlikeresetOpenNodes). Under controlledopenNodesthey're no-ops — the parent owns the slot.collectBranchKeys(data, keySeparator?)helper. Public export of the traversal thatexpandAll()uses internally. Controlled-openNodesconsumers can build their own expand-all:setOpen(collectBranchKeys(data))without re-implementing the path-join walk.keySeparatorprop. Customize the character that joins node keys into paths (default"/"). Useful when node keys themselves contain/(URLs, filesystem paths) — pass e.g."."and the library uses that everywhere: emittedItem.keypaths,openNodeslookups, LEFT-arrow parent-focus navigation, andunflatten(items, keySeparator)for custom render-props.unflatten()gains a matching optional second argument.unflattenhelper +UnflattenResult<T>type. Public export of the grouping functiondefaultChildrenuses internally — reconstructs a nested tree from the flatitems[]via slash-joined key paths. Generic overT extends { key: string }. Custom render-props that want to emit a nested<ul>/<li>/<ul>DOM can now call one library function instead of copy-pasting the algorithm.- Tailwind v4 theming out of the box. Every color/typography
CSS custom property resolves via a
var()chain that prefers Tailwind v4's auto-exposed theme variables (--color-primary,--font-sans,--color-gray-*). Tailwind v4 users' brands flow through with zero config. - CSS subpath export. Import styles via
react-simple-tree-menu/styles. The legacyreact-simple-tree-menu/dist/main.cssdeep-import path from v1 is removed — update to the subpath export above (same byte content, ergonomic import). - WAI-ARIA tree pattern on rendered DOM: see above.
- Zero-dep bundle.
dependencies: {}. The library ships no transitive npm packages — consumers install 0 packages beyond React itself. - Posinset / setsize now present on every
Itemfor consumers who want ARIA sibling coordinates in custom render-props. - Modern default stylesheet: rounded corners, subtle hover, inset focus ring. All values themable via CSS custom properties.
classnamesruntime dependency.fast-memoizeruntime dependency.is-emptyruntime dependency.tiny-debounceruntime dependency.
Consumers don't need to uninstall — these were transitive.
- Class component → function component with
useReduceruseImperativeHandle. Behavior preserved; tested under a dual-entry characterization suite during the strangulation window.
- Tree flattening (
walk) rewritten for the closed-branch short-circuit: walking a 100k-node fully-collapsed tree runs in microseconds. ItemComponentis nowReact.memo-wrapped.
- ESM-first published package with proper
exportsfield. - Types ship alongside:
dist/index.d.ts. - CI: GitHub Actions matrix on Node 20 × React 16.14, 17, 18, 19.
- Test stack: Vitest + React Testing Library (replaces Jest + Enzyme).
- Build: tsup (replaces Rollup 1).
- Lint: ESLint 9 flat config.
- Bundle size: 3.01 KB minified + brotli
(enforced via
size-limitin CI, 3.5 KB budget).
- Search input is now controlled internally — clearing
searchTermviaresetOpenNodesvisibly empties the input, fixing a v1 bug where reset left stale text in the box. - Object-format
TreeNode's sortindexno longer leaks onto emittedItemobjects. ItemComponentno longer spreads arbitrary user props onto the rendered<li>as HTML attributes (React'skeywarning, invalid data-like attributes).
See git history before commit 44c0de3.