NavTree
href render as real <a> elements on web, so cmd-click, middle-click, "copy link address" and crawlers all work; a plain left-click goes to onNavigate for client-side routing. Omit onNavigate and the rows stay ordinary links the browser follows.tree/treeitem roles come along with it.Examples
(4)Properties
(15)Playground
Grouped routes
activeHref opened it.Loading demo…
import
React
,
{
useState
}
from
'react'
;
import
{
NavTree
,
type
NavTreeItem
}
from
'@platform-blocks/react-ui-library'
;
// The whole input: a flat list, with a category on each row. Nothing here
// describes the tree — `NavTree` derives it.
const
ROUTES
:
NavTreeItem
[
]
=
[
{
label
:
'Getting Started'
,
href
:
'/getting-started'
}
,
{
label
:
'Button'
,
href
:
'/components/Button'
,
group
:
[
'Components'
,
'Input'
]
}
,
{
label
:
'Select'
,
href
:
'/components/Select'
,
group
:
[
'Components'
,
'Input'
]
}
,
{
label
:
'Checkbox'
,
href
:
'/components/Checkbox'
,
group
:
[
'Components'
,
'Input'
]
}
,
{
label
:
'Card'
,
href
:
'/components/Card'
,
group
:
[
'Components'
,
'Display'
]
}
,
{
label
:
'Badge'
,
href
:
'/components/Badge'
,
group
:
[
'Components'
,
'Display'
]
}
,
{
label
:
'Tabs'
,
href
:
'/components/Tabs'
,
group
:
[
'Components'
,
'Navigation'
]
}
,
]
;
export
function
Demo
(
)
{
const
[
route
,
setRoute
]
=
useState
(
'/components/Select'
)
;
return
(
<NavTree
items
=
{
ROUTES
}
activeHref
=
{
route
}
onNavigate
=
{
item
=>
setRoute
(
item
.
href
)
}
showGuides
/>
)
;
}
Counts and order
groupOrder curates the sections that matter and leaves the rest alphabetical. renderEndSection hangs a count off each branch, and openDepth={0} starts everything closed.Loading demo…
import
React
,
{
useState
}
from
'react'
;
import
{
Badge
,
NavTree
,
type
NavTreeItem
}
from
'@platform-blocks/react-ui-library'
;
const
ROUTES
:
NavTreeItem
[
]
=
[
{
label
:
'Button'
,
href
:
'/components/Button'
,
group
:
'Input'
}
,
{
label
:
'Select'
,
href
:
'/components/Select'
,
group
:
'Input'
}
,
{
label
:
'Checkbox'
,
href
:
'/components/Checkbox'
,
group
:
'Input'
}
,
{
label
:
'Card'
,
href
:
'/components/Card'
,
group
:
'Display'
}
,
{
label
:
'Badge'
,
href
:
'/components/Badge'
,
group
:
'Display'
}
,
{
label
:
'Tabs'
,
href
:
'/components/Tabs'
,
group
:
'Navigation'
}
,
]
;
export
function
Demo
(
)
{
const
[
route
,
setRoute
]
=
useState
(
'/components/Card'
)
;
return
(
<NavTree
items
=
{
ROUTES
}
activeHref
=
{
route
}
onNavigate
=
{
item
=>
setRoute
(
item
.
href
)
}
// Curate the order that matters and let the rest sort themselves.
groupOrder
=
{
[
'Input'
,
'Display'
]
}
openDepth
=
{
0
}
renderEndSection
=
{
node
=>
node
.
children
?
<Badge
size
=
"xs"
variant
=
"light"
>
{
node
.
children
.
length
}
</Badge
>
:
null
}
/>
)
;
}
Filtering
searchable adds a filter field wired to the tree. Typing hides the rows that do not match, opens the branches above the ones that do, and marks the matched substring — past a certain length, three letters beat any amount of nesting.Loading demo…
import
React
,
{
useState
}
from
'react'
;
import
{
NavTree
,
type
NavTreeItem
}
from
'@platform-blocks/react-ui-library'
;
const
ROUTES
:
NavTreeItem
[
]
=
[
{
label
:
'Button'
,
href
:
'/components/Button'
,
group
:
'Input'
}
,
{
label
:
'Checkbox'
,
href
:
'/components/Checkbox'
,
group
:
'Input'
}
,
{
label
:
'Select'
,
href
:
'/components/Select'
,
group
:
'Input'
}
,
{
label
:
'TextArea'
,
href
:
'/components/TextArea'
,
group
:
'Input'
}
,
{
label
:
'Badge'
,
href
:
'/components/Badge'
,
group
:
'Display'
}
,
{
label
:
'Card'
,
href
:
'/components/Card'
,
group
:
'Display'
}
,
{
label
:
'Breadcrumbs'
,
href
:
'/components/Breadcrumbs'
,
group
:
'Navigation'
}
,
{
label
:
'Tabs'
,
href
:
'/components/Tabs'
,
group
:
'Navigation'
}
,
]
;
export
function
Demo
(
)
{
const
[
route
,
setRoute
]
=
useState
(
'/components/Card'
)
;
return
(
<NavTree
items
=
{
ROUTES
}
activeHref
=
{
route
}
onNavigate
=
{
item
=>
setRoute
(
item
.
href
)
}
searchable
searchPlaceholder
=
"Filter components…"
/>
)
;
}
Collapsed rail
collapsed drops the sidebar to a strip of top-level icons — the group holding the current route stays marked, and pressing one lands on the first page inside it. A sidebar with a hundred routes shows a handful of icons here, not a hundred.Loading demo…
import
React
,
{
useState
}
from
'react'
;
import
{
Column
,
Icon
,
NavTree
,
Switch
,
type
NavTreeItem
}
from
'@platform-blocks/react-ui-library'
;
const
ROUTES
:
NavTreeItem
[
]
=
[
{
label
:
'Button'
,
href
:
'/components/Button'
,
group
:
'Components'
}
,
{
label
:
'Card'
,
href
:
'/components/Card'
,
group
:
'Components'
}
,
{
label
:
'LineChart'
,
href
:
'/components/LineChart'
,
group
:
'Charts'
}
,
{
label
:
'BarChart'
,
href
:
'/components/BarChart'
,
group
:
'Charts'
}
,
{
label
:
'useHover'
,
href
:
'/hooks/useHover'
,
group
:
'Hooks'
}
,
]
;
const
GROUP_ICONS
=
{
Components
:
<Icon
name
=
"grid"
size
=
{
18
}
/>
,
Charts
:
<Icon
name
=
"chart-bar"
size
=
{
18
}
/>
,
Hooks
:
<Icon
name
=
"hook"
size
=
{
18
}
/>
,
}
;
export
function
Demo
(
)
{
const
[
collapsed
,
setCollapsed
]
=
useState
(
true
)
;
const
[
route
,
setRoute
]
=
useState
(
'/components/Card'
)
;
return
(
<Column
gap
=
"md"
>
<Switch
checked
=
{
collapsed
}
onChange
=
{
setCollapsed
}
label
=
"Collapsed"
/>
<NavTree
items
=
{
ROUTES
}
activeHref
=
{
route
}
onNavigate
=
{
item
=>
setRoute
(
item
.
href
)
}
groupIcons
=
{
GROUP_ICONS
}
collapsed
=
{
collapsed
}
/>
</Column
>
)
;
}
</>
react-ui-library
100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.
A Platform Blocks product.
Quick Links
Resources