A SideNav provides users with a way to navigate nested hierarchical set of links.
Content
SideNav follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items prop, and a recursive function to render the children.
import {SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, Collection} from '@react-spectrum/s2/SideNav';
import {RoutedSideNav} from './RoutedSideNav';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
<RoutedSideNav defaultSelectedRoute="/notes">
{({selectedRoute}) => (
<SideNav
aria-label="Files"
styles={style({height: 260, width: 210})}
selectedRoute={selectedRoute}
defaultExpandedKeys={[1, 2, 5]}
items={items}>
{function renderItem(item) {
return (
<SideNavItem textValue={item.title} href={item.href}>
<SideNavItemContent>{item.href ? <SideNavItemLink>{item.title}</SideNavItemLink> : item.title}</SideNavItemContent>
{/* recursively render children */}
{item.children && <Collection items={item.children}>
{renderItem}
</Collection>}
</SideNavItem>
);
}}
</SideNav>
)}
</RoutedSideNav>
Slots
SideNavItemContent supports icons, Text, SideNavItemLink, ActionMenu, and ActionButtonGroup as children.
import {SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, Collection, Text} from '@react-spectrum/s2/SideNav';
import {RoutedSideNav} from './RoutedSideNav';
import {ActionMenu, MenuItem} from '@react-spectrum/s2/ActionMenu';
import Folder from '@react-spectrum/s2/icons/Folder';
import File from '@react-spectrum/s2/icons/File';
import Edit from '@react-spectrum/s2/icons/Edit';
import Delete from '@react-spectrum/s2/icons/Delete';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
<RoutedSideNav defaultSelectedRoute="/notes">
{({selectedRoute}) => (
<SideNav
aria-label="Files"
styles={style({height: 300, width: 210})}
selectedRoute={selectedRoute}
defaultExpandedKeys={[1, 2, 5]}
items={items}>
{function renderItem(item) {
return (
<SideNavItem textValue={item.title} href={item.href}>
<SideNavItemContent>
{
item.href ? <SideNavItemLink>
{item.type === 'directory' ? <Folder /> : <File />}
<Text>{item.title}</Text>
</SideNavItemLink> : <>
{item.type === 'directory' ? <Folder /> : <File />}
<Text>{item.title}</Text>
</>
}
<ActionMenu>
<MenuItem>
<Edit />
<Text>Edit</Text>
</MenuItem>
<MenuItem>
<Delete />
<Text>Delete</Text>
</MenuItem>
</ActionMenu>
</SideNavItemContent>
{item.children && <Collection items={item.children}>
{renderItem}
</Collection>}
</SideNavItem>
);
}}
</SideNav>
)}
</RoutedSideNav>
Routing
SideNavs do not support an uncontrolled selection state, you are responsible for managing it through the selectedRoute prop. You may wire this up
to a router or other state management solution. The example below derives selectedRoute from the router's current location,
and provides React Aria's RouterProvider with the router's navigate function so that clicking an item performs a client side navigation.
If a SideNavItem has an href, then you must pass a SideNavItemLink as a child of the SideNavItemContent.
See Routing setup in Getting Started for more info.
import {SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, Text} from '@react-spectrum/s2/SideNav';
import {RouterProvider} from 'react-aria-components';
import {MemoryRouter, useLocation, useNavigate} from './router';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import CCLibrary from '@react-spectrum/s2/icons/CCLibrary';
import Files from '@react-spectrum/s2/icons/Files';
import Images from '@react-spectrum/s2/icons/Images';
function FilesSideNav() {
let navigate = useNavigate();
let {pathname} = useLocation();
return (
<RouterProvider navigate={navigate}>
<SideNav aria-label="Files" defaultExpandedKeys={['your-libraries']} selectedRoute={pathname} styles={style({height: 120, width: 210})}>
<SideNavItem href="/files" textValue="Files">
<SideNavItemContent>
<SideNavItemLink>
<Files />
<Text>Files</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
<SideNavItem id="your-libraries" href="/your-libraries" textValue="Your Libraries">
<SideNavItemContent>
<SideNavItemLink>
<CCLibrary />
<Text>Your Libraries</Text>
</SideNavItemLink>
</SideNavItemContent>
<SideNavItem id="photos" href="/photos" textValue="Photos">
<SideNavItemContent>
<SideNavItemLink>
<Images />
<Text>Photos</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
</SideNavItem>
</SideNav>
</RouterProvider>
);
}
<MemoryRouter initialEntries={['/files']}>
<FilesSideNav />
</MemoryRouter>
Sections
A SideNav can contain sections to group items together. They are non-collapsible and non-interactive.
import {SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, SideNavSection, SideNavHeader, Text} from '@react-spectrum/s2/SideNav';
import {RoutedSideNav} from './RoutedSideNav';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import UserGroup from '@react-spectrum/s2/icons/UserGroup';
import CCLibrary from '@react-spectrum/s2/icons/CCLibrary';
import Files from '@react-spectrum/s2/icons/Files';
import Images from '@react-spectrum/s2/icons/Images';
import Animation from '@react-spectrum/s2/icons/Animation';
import Download from '@react-spectrum/s2/icons/Download';
import Apps from '@react-spectrum/s2/icons/Apps';
<RoutedSideNav defaultSelectedRoute="/files">
{({selectedRoute}) => (
<SideNav aria-label="Files" selectedRoute={selectedRoute} styles={style({height: 260, width: 210})}>
<SideNavSection>
<SideNavHeader>Favorites</SideNavHeader>
<SideNavItem href="/applications" textValue="Applications">
<SideNavItemContent>
<SideNavItemLink>
<Apps />
<Text>Applications</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
<SideNavItem href="/downloads" textValue="Downloads">
<SideNavItemContent>
<SideNavItemLink>
<Download />
<Text>Downloads</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
</SideNavSection>
<SideNavSection>
<SideNavHeader>Workspaces</SideNavHeader>
<SideNavItem href="/files" textValue="Files">
<SideNavItemContent>
<SideNavItemLink>
<Files />
<Text>Files</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
<SideNavItem href="/your-libraries" textValue="Your Libraries">
<SideNavItemContent>
<SideNavItemLink>
<CCLibrary />
<Text>Your Libraries</Text>
</SideNavItemLink>
</SideNavItemContent>
<SideNavItem href="/photos" textValue="Photos">
<SideNavItemContent>
<SideNavItemLink>
<Images />
<Text>Photos</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
</SideNavItem>
<SideNavItem href="/shared-with-you" id="shared-with-you" textValue="Shared with You">
<SideNavItemContent>
<SideNavItemLink>
<UserGroup />
<Text>Shared with You</Text>
</SideNavItemLink>
</SideNavItemContent>
<SideNavItem href="/animations" textValue="Animations">
<SideNavItemContent>
<SideNavItemLink>
<Animation />
<Text>Animations</Text>
</SideNavItemLink>
</SideNavItemContent>
</SideNavItem>
</SideNavItem>
</SideNavSection>
</SideNav>
)}
</RoutedSideNav>
API
<SideNav>
<SideNavItem>
<SideNavItemContent>
<Icon />
<Text />
<ActionMenu /> or <ActionButtonGroup />
</SideNavItemContent>
</SideNavItem>
<SideNavItem href>
<SideNavItemContent>
<SideNavItemLink>
<Icon />
<Text />
</SideNavItemLink>
<ActionMenu /> or <ActionButtonGroup />
</SideNavItemContent>
</SideNavItem>
</SideNav>
SideNav
| Name | Type | |
|---|---|---|
selectedRoute | string | null | |
The route that is currently selected. | ||
styles | StylesPropWithHeight | |
Spectrum-defined styles, returned by the | ||
expandedKeys | Iterable | |
The currently expanded keys in the collection (controlled). | ||
defaultExpandedKeys | Iterable | |
The initial expanded keys in the collection (uncontrolled). | ||
children | ReactNode | | |
The contents of the collection. | ||
items | Iterable | |
Item objects in the collection. | ||
dependencies | ReadonlyArray | |
Values that should invalidate the item cache when using dynamic collections. | ||
disabledKeys | Iterable | |
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | ||
SideNavItem
| Name | Type | |
|---|---|---|
textValue | string | |
A string representation of the side nav item's contents, used for features like typeahead. | ||
hasChildItems | boolean | |
Whether this item has children. | ||
id | Key | |
The unique id of the tree row. | ||
children | ReactNode | |
The content of the tree item along with any nested children. Supports static nested tree items or use of a Collection to dynamically render nested tree items. | ||
isDisabled | boolean | |
Whether the item is disabled. | ||
SideNavItemContent
| Name | Type | |
|---|---|---|
children | ReactNode | |
Rendered contents of the side nav item or child items. | ||
SideNavItemLink
| Name | Type | |
|---|---|---|
children | ReactNode | |
Rendered contents of the link. | ||
SideNavSection
| Name | Type | |
|---|---|---|
id | Key | |
The unique id of the section. | ||
children | ReactNode | | |
Static child items or a function to render children. | ||
items | Iterable | |
Item objects in the section. | ||
dependencies | ReadonlyArray | |
Values that should invalidate the item cache when using dynamic collections. | ||
SideNavHeader
| Name | Type | |
|---|---|---|
children | ReactNode | |
The children of the component. | ||