Tip
This is a feature release with no breaking changes. All existing props and behavior remain fully backward compatible.
New Features
1. Redesigned toolbar
The header area has been redesigned into a proper toolbar with modern icons and additional controls. The expand/collapse icons have been replaced with IconArrowBarToDown / IconArrowBarToUp (previously IconLibraryPlus / IconLibraryMinus), and buttons now use variant="subtle" with consistent size="sm" for a cleaner appearance.
The toolbar groups all action buttons on the right side of the header, while the title and badge sit on the left. The header is now displayed whenever any toolbar prop is enabled — even without an explicit title.
2. Search with tree filtering
The new withSearch prop adds a search toggle button to the toolbar. When activated, a search bar appears between the header and the tree. Typing a query performs case-insensitive matching against both key names and formatted values.
How it works:
- Tree filtering: non-matching branches are removed from the tree entirely (not just dimmed), keeping the view clean and focused
- Text highlighting: matching portions of keys and values are highlighted with an amber background via the
searchHighlightStyles API selector - Row highlighting: rows with direct matches get a subtle amber background (
rgba(251, 191, 36, 0.15)) - Auto-expand: ancestor nodes of matches are automatically expanded to reveal results
- State preservation: the pre-search expanded state is saved and restored when the search is cleared or closed
- Debounced input: search queries are debounced (default 300ms) to avoid excessive re-renders
New search-related props:
| Prop | Type | Default | Description |
|---|---|---|---|
withSearch |
boolean |
false |
Show the search toggle button |
searchIcon |
ReactNode |
<IconSearch /> |
Custom search toggle icon |
searchPlaceholder |
string |
'Filter keys and values...' |
Search input placeholder |
searchQuery |
string |
— | Controlled search query |
onSearchChange |
(query: string) => void |
— | Callback when search query changes |
searchDebounce |
number |
300 |
Debounce delay in milliseconds |
New utility functions exported from lib/utils.tsx:
searchTree(nodes, query)— returnsmatchedPaths,directMatches, andexpandedPathsfilterTreeBySearch(nodes, matchedPaths)— prunes the tree to keep only matched branches and their ancestors
3. Paper wrapper via withBorder
The new withBorder prop wraps the entire component in a Mantine <Paper withBorder> element, providing a clean bordered container out of the box. The border radius is configurable via borderRadius (default 'sm').
| Prop | Type | Default | Description |
|---|---|---|---|
withBorder |
boolean |
false |
Wrap in Paper with border |
borderRadius |
MantineRadius |
'sm' |
Paper border radius |
4. Custom root node label via rootName
The new rootName prop allows customizing the label displayed for the root node of the tree (default 'root'). This is useful when the JSON data represents a named entity such as "response", "config", or "user".
| Prop | Type | Default | Description |
|---|---|---|---|
rootName |
string |
'root' |
Label for the root node |
5. Key count badge
The new withKeyCountBadge prop displays a badge next to the title showing the total number of top-level keys (for objects) or items (for arrays). The label is customizable via keyCountBadgeLabel.
| Prop | Type | Default | Description |
|---|---|---|---|
withKeyCountBadge |
boolean |
false |
Show key/item count badge |
keyCountBadgeLabel |
(count: number) => string |
— | Custom badge label formatter |
6. Global copy-to-clipboard
The new withCopyAll prop adds a button to the toolbar that copies the entire JSON data (JSON.stringify(data, null, 2)) to the clipboard.
| Prop | Type | Default | Description |
|---|---|---|---|
withCopyAll |
boolean |
false |
Show global copy button |
copyAllIcon |
ReactNode |
<IconCopy /> |
Custom copy-all icon |
onCopyAll |
(json: string) => void |
— | Callback when JSON is copied |
7. Copy feedback (green check icon)
Both the global copy button and per-node copy buttons now show a brief green check icon (IconCheck) after a successful clipboard write. The feedback lasts 1.5 seconds before reverting to the original icon. This is implemented via the new CopyNodeButton internal component which manages its own copied state and cleanup timer.
New Styles API Selectors
8 new selectors have been added for styling the toolbar and search features:
| Selector | Description |
|---|---|
paper |
Paper wrapper element (when withBorder is true) |
toolbar |
Toolbar button group on the right side of the header |
keyCountBadge |
Key count badge next to the title |
copyAllButton |
Global copy JSON button in toolbar |
searchToggle |
Search toggle button in toolbar |
searchBar |
Search bar container between header and tree |
searchInput |
Search text input |
searchHighlight |
Highlighted matching text in search results |
New CSS Variable
| Variable | Selector | Default | Description |
|---|---|---|---|
--json-tree-search-highlight-color |
searchHighlight |
rgba(251, 191, 36, 0.4) |
Background color for search match highlights (slightly dimmer in dark mode) |
Internal Changes
Nullish coalescing in tree data conversion
The convertToTreeData function in lib/utils.tsx now uses nullish coalescing (??) instead of logical OR (||) when falling back to the path value for node labels. This correctly handles cases where the key is an empty string or 0 (both falsy but valid keys).
Refactored expand/collapse handlers
The expand-all and collapse-all logic has been extracted from inline arrow functions into named handleExpandAll and handleCollapseAll callbacks wrapped in useCallback, improving readability and memoization.
New internal component: CopyNodeButton
Per-node copy buttons are now rendered by a CopyNodeButton component that encapsulates the copied/feedback state. The handleCopy function now returns Promise<boolean> to communicate success/failure to the button.
data-searching attribute
The root element receives a data-searching attribute when a search query is active, enabling CSS-based conditional styling.
Tests
16 new tests added (from 35 to 51 total):
Toolbar tests (11):
- Paper wrapper rendering (present/absent)
- Key count badge for objects and arrays
- Custom
keyCountBadgeLabel - No badge for primitive data
- Copy-all button rendering
- Search toggle rendering
- Header visibility when any toolbar prop is set
- Search bar opening on toggle click
- All toolbar features combined
rootNamecustom labelrootNamedefault value
Search utility tests (5):
searchTreefinds matches by key namesearchTreefinds matches by valuesearchTreereturns empty for empty queryfilterTreeBySearchkeeps only matching branchessearchTreeis case insensitive
Documentation
- Configurator demo updated with all new props (
withBorder,withKeyCountBadge,withCopyAll,withSearch,rootName), with sensible defaults enabled - New demo:
rootName— shows custom root labels for objects and arrays - New demo:
search— shows full toolbar with search, expand-all, copy-all, and key count badge on a realistic API response payload - Styles API demo updated with all toolbar features and
showLineNumbersenabled - Styles API definitions updated with all 8 new selectors and the
--json-tree-search-highlight-colorCSS variable - CLAUDE.md updated with toolbar and search architecture documentation
All New Props Summary
| Prop | Type | Default |
|---|---|---|
rootName |
string |
'root' |
withBorder |
boolean |
false |
borderRadius |
MantineRadius |
'sm' |
withKeyCountBadge |
boolean |
false |
keyCountBadgeLabel |
(count: number) => string |
— |
withCopyAll |
boolean |
false |
copyAllIcon |
ReactNode |
<IconCopy /> |
onCopyAll |
(json: string) => void |
— |
withSearch |
boolean |
false |
searchIcon |
ReactNode |
<IconSearch /> |
searchPlaceholder |
string |
'Filter keys and values...' |
searchQuery |
string |
— |
onSearchChange |
(query: string) => void |
— |
searchDebounce |
number |
300 |
Commits
| Hash | Date | Message |
|---|---|---|
b64bf06 |
2026-04-11 | feat: redesign toolbar with counter badge, copy all, search toggle, Paper wrapper |
5d02bd5 |
2026-04-11 | feat: add tests, storybook stories, and update demo configurator for toolbar upgrade |
1e55bd2 |
2026-04-11 | feat: implement search with highlight, auto-expand, and dimming (Phase 2) |
f332b64 |
2026-04-11 | fix: improve search highlight visibility and story defaults |
472c843 |
2026-04-11 | fix: use inline opacity for search dimming instead of CSS selectors |
7f693a0 |
2026-04-12 | feat: add rootName prop + switch search from dimming to filtering |
4e27291 |
2026-04-12 | feat: update docs, demos, README, CLAUDE.md for toolbar upgrade + search + rootName |
63c2d3f |
2026-04-12 | fix: enable all toolbar features in Styles API demo |
e358a97 |
2026-04-12 | feat: add copy feedback (green check icon) for global and per-node copy |
745c0a4 |
2026-04-12 | fix: address Copilot review — nullish check, copy feedback robustness, search tests |
430a5f9 |
2026-04-12 | Release 3.1.0 |
What's Changed
Full Changelog: 3.0.0...3.1.0