XDSSideNav@xds/core · SideNav
Usage
A sidebar navigation component for organizing application pages with sections, nested items, and icons. Use SideNav as the primary navigation when an app has 5 or more destinations or requires hierarchical grouping.Best practices
| Guidance | Practices |
|---|---|
| Do | Use sections to group related navigation items and help users scan for their destination. |
| Do | Pair outline and filled icon variants so the selected state is visually distinct. |
| Don't | Include a SideNavHeading when a TopNav is already providing app identity — this duplicates branding. |
| Don't | Use for filtering content — use tabs or filter buttons instead. |
Anatomy
| Element | Description | |
|---|---|---|
| Product icon and name | Branding area at the top of the nav. | |
| Navigation items | required | Sections and groups of navigable links. |
| Collapse/expand toggle | Toggle to collapse or expand the side nav. |
Import
tsimport {XDSSideNav} from '@xds/core/SideNav'
Props
| Prop | Type | Description |
|---|---|---|
header | ReactNode | Header area (typically XDSSideNavHeading). Sticky. |
topContent | ReactNode | Content below the header, e.g., a create button. |
children | ReactNode | Navigation sections and items. Scrollable. |
footer | ReactNode | Footer area above the icon bar. |
footerIcons | ReactNode | Footer icon bar. |
collapsible | boolean | { defaultIsCollapsed?: boolean; isCollapsed?: boolean; onCollapsedChange?: (isCollapsed: boolean) => void; hasButton?: boolean; buttonLabel?: string } (default: false) | Enables collapse behavior. true for uncontrolled with default toggle button, or an object for controlled mode and advanced config (defaultIsCollapsed, isCollapsed + onCollapsedChange, hasButton, buttonLabel). |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
Sub-components
SideNav is a compound component with 5 sub-components.XDSSideNav
Container with five zones: header, topContent, children (scrollable), footer, and footerIcons. Supports collapsible mode.| Prop | Type | Description |
|---|---|---|
header | ReactNode | Header area (typically XDSSideNavHeading). Sticky. |
topContent | ReactNode | Content below the header, e.g., a create button. |
children | ReactNode | Navigation sections and items. Scrollable. |
footer | ReactNode | Footer area above the icon bar. |
footerIcons | ReactNode | Footer icon bar. |
collapsible | boolean | { defaultIsCollapsed?: boolean; isCollapsed?: boolean; onCollapsedChange?: (isCollapsed: boolean) => void; hasButton?: boolean; buttonLabel?: string } (default: false) | Enables collapse behavior. true for uncontrolled with default toggle button, or an object for controlled mode and advanced config (defaultIsCollapsed, isCollapsed + onCollapsedChange, hasButton, buttonLabel). |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
XDSSideNavCollapseButton
Toggle button for sidenav collapse. Place inside XDSSideNav (reads context automatically) or outside (pass sideNavRef). Renders as an icon-only ghost button by default.| Prop | Type | Description |
|---|---|---|
sideNavRef | RefObject<HTMLElement | null> | Ref to the XDSSideNav element. Only needed when the button is rendered outside the sidenav. |
label | string | Custom button label. When provided, renders as a text button with chevron. When omitted, renders icon-only. |
children | ReactNode | Custom button content. Overrides the default chevron icon and label. |
XDSSideNavHeading
Product/suite/account heading with smart interaction boundary logic for links and a menu popover.| Prop | Type | Description |
|---|---|---|
headingrequired | string | Product/app name. |
icon | ReactNode | Product/app icon. |
headingHref | string | Link for the heading. |
superheading | string | Text above the heading. |
superheadingHref | string | Link for the superheading. |
subheading | string | Text below the heading. |
subheadingHref | string | Link for the subheading. |
menu | ReactNode | Menu content rendered inside a popover. |
headerEndContent | ReactNode | Content rendered at the trailing edge of the heading row, between text and chevron. Useful for badges, status indicators, or compact action buttons. Hidden when collapsed. |
XDSSideNavItem
Navigation item with icon, selected state, optional end content, and nesting support via children.| Prop | Type | Description |
|---|---|---|
labelrequired | string | Item label. |
as | XDSLinkComponentType | Custom link component. |
icon | XDSIconType | Icon displayed in the outline (unselected) variant. See `npx xds docs icons` for valid semantic names. |
selectedIcon | XDSIconType | Icon displayed when the item is selected (filled variant). See `npx xds docs icons` for valid semantic names. |
isSelected | boolean (default: false) | Marks this item as the current page. |
isDisabled | boolean (default: false) | Disabled state. |
href | string | Navigation URL. |
onClick | (e: MouseEvent) => void | Click handler. |
endContent | ReactNode | Right-side content such as badges or counts. |
children | ReactNode | Sub-items for nesting. |
collapsible | boolean | { defaultIsCollapsed?: boolean, isCollapsed?: boolean, onCollapsedChange?: (isCollapsed: boolean) => void } (default: false) | Enables collapse behavior for items with children. Pass true for uncontrolled (starts expanded), or an object for controlled mode. |
XDSSideNavSection
Section grouping with an optional title, subtitle, and end content.| Prop | Type | Description |
|---|---|---|
titlerequired | string | Section title. |
subtitle | string | Section subtitle. |
children | ReactNode | Section items. |
endContent | ReactNode | Right-side content in the section header. |
isHeaderHidden | boolean (default: false) | Visually hides the section header while keeping it accessible to screen readers. |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
Examples
Common configurations, variations, and states.SideNav — End ContentSide navigation items with badges, counts, and context menus as trailing content.
tsx'use client';import type {ComponentProps} from 'react';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/core/SideNav';import {XDSBadge} from '@xds/core/Badge';import {XDSText} from '@xds/core/Text';import {XDSMoreMenu} from '@xds/core/MoreMenu';function HomeIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>);}function FolderIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z" /></svg>);}function ChartBarIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" /></svg>);}function UserGroupIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" /></svg>);}export default function SideNavEndContent() {return (<XDSSideNavheader={<XDSSideNavHeading heading="My App" headingHref="/" />}><XDSSideNavSection title="Navigation" isHeaderHidden><XDSSideNavItemlabel="Dashboard"icon={HomeIcon}isSelectedhref="/dashboard"endContent={<XDSMoreMenusize="sm"items={[{label: 'Pin to top', onClick: () => {}},{label: 'Rename', onClick: () => {}},]}/>}/><XDSSideNavItemlabel="Projects"icon={FolderIcon}href="/projects"endContent={<XDSBadge label={12} />}/><XDSSideNavItemlabel="Analytics"icon={ChartBarIcon}href="/analytics"endContent={<XDSBadge label="New" />}/><XDSSideNavItemlabel="Team"icon={UserGroupIcon}href="/team"endContent={<XDSText type="supporting" color="secondary">8 members</XDSText>}/></XDSSideNavSection></XDSSideNav>);}
SideNav — Header with MenuSide navigation with an account switcher dropdown in the header for multi-account apps.
tsx'use client';import type {ComponentProps} from 'react';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/core/SideNav';import {XDSListItem} from '@xds/core/List';import {XDSNavIcon} from '@xds/core/NavIcon';function HomeIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>);}function CogIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>);}export default function SideNavWithHeaderMenu() {return (<XDSSideNavheader={<XDSSideNavHeadingicon={<XDSNavIconicon={<svgstyle={{width: 16, height: 16}}fill="none"viewBox="0 0 24 24"strokeWidth={1.5}stroke="currentColor"><pathstrokeLinecap="round"strokeLinejoin="round"d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9"/></svg>}/>}heading="Product Name"subheading="Business Account"menu={<><XDSListItem label="Personal Account" href="#" /><XDSListItem label="Acme Corp" href="#" /><XDSListItem label="Add account" href="#" /><XDSListItem label="Sign out" href="#" /></>}/>}><XDSSideNavSection title="Navigation"><XDSSideNavItem label="Dashboard" icon={HomeIcon} isSelected /><XDSSideNavItem label="Settings" icon={CogIcon} /></XDSSideNavSection></XDSSideNav>);}
SideNav — Nested ItemsSide navigation with collapsible nested items for settings or hierarchical menus.
tsx'use client';import type {ComponentProps} from 'react';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/core/SideNav';function HomeIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>);}function CogIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>);}export default function SideNavNestedItems() {return (<XDSSideNav header={<XDSSideNavHeading heading="My App" />}><XDSSideNavSection title="Main"><XDSSideNavItem label="Dashboard" icon={HomeIcon} isSelected /><XDSSideNavItem label="Settings" icon={CogIcon}><XDSSideNavItem label="General" href="/settings/general" /><XDSSideNavItem label="Security" href="/settings/security" /><XDSSideNavItemlabel="Notifications"href="/settings/notifications"/></XDSSideNavItem></XDSSideNavSection></XDSSideNav>);}
Showcase source
tsx'use client';import type {ComponentProps} from 'react';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/core/SideNav';function HomeIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>);}function FolderIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z" /></svg>);}function ChartBarIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" /></svg>);}function DocumentTextIcon(props: ComponentProps<'svg'>) {return (<svg fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}><path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" /></svg>);}export default function SideNavShowcase() {return (<XDSSideNavheader={<XDSSideNavHeading heading="My App" headingHref="/" />}><XDSSideNavSection title="Main"><XDSSideNavItemlabel="Dashboard"icon={HomeIcon}isSelectedhref="/dashboard"/><XDSSideNavItemlabel="Projects"icon={FolderIcon}href="/projects"/><XDSSideNavItemlabel="Analytics"icon={ChartBarIcon}href="/analytics"/></XDSSideNavSection><XDSSideNavSection title="Documents"><XDSSideNavItemlabel="All Documents"icon={DocumentTextIcon}href="/documents"/></XDSSideNavSection></XDSSideNav>);}