alpha

SideNavPanel

A SideNavPanel contains a SideNav and other app chrome in a container that collapses to an icon rail.

A SideNavPanel wraps a SideNav, along with any other app chrome, in a container that collapses down to a narrow icon rail. It renders its own collapse/expand toggle at the bottom of the panel, and animates its width between the two states.

The expanded width comes from the styles prop you pass to SideNavPanel.

Acme Cloud
Your account
/files
Example
SideNavPanelApp.tsx
import {SideNavPanel, SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, SideNavSection, SideNavHeader, Text} from '@react-spectrum/s2/SideNavPanel';
import {SideNavPanelApp} from './SideNavPanelApp';
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';
import Project from '@react-spectrum/s2/icons/Project';

<SideNavPanelApp defaultSelectedRoute="/files">
  {({selectedRoute}) => (
    <SideNavPanel
      defaultCollapsed
      styles={style({gridArea: 'sidebar', width: 208, marginX: 8})}>
      <SideNav
        aria-label="Files"
        selectedRoute={selectedRoute}
        defaultExpandedKeys={['projects']}
        styles={style({width: 'full'})}>
        <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 href="/photos" textValue="Photos">
            <SideNavItemContent>
              <SideNavItemLink>
                <Images />
                <Text>Photos</Text>
              </SideNavItemLink>
            </SideNavItemContent>
          </SideNavItem>
        </SideNavItem>
        <SideNavSection>
          <SideNavHeader>Work</SideNavHeader>
          <SideNavItem id="projects" href="/projects" textValue="Projects">
            <SideNavItemContent>
              <SideNavItemLink>
                <Project />
                <Text>Projects</Text>
              </SideNavItemLink>
            </SideNavItemContent>
            <SideNavItem href="/projects/website" textValue="Website redesign">
              <SideNavItemContent>
                <SideNavItemLink>
                  <Text>Website redesign</Text>
                </SideNavItemLink>
              </SideNavItemContent>
            </SideNavItem>
            <SideNavItem href="/projects/mobile" textValue="Mobile app">
              <SideNavItemContent>
                <SideNavItemLink>
                  <Text>Mobile app</Text>
                </SideNavItemLink>
              </SideNavItemContent>
            </SideNavItem>
          </SideNavItem>
        </SideNavSection>
      </SideNav>
    </SideNavPanel>
  )}
</SideNavPanelApp>

Collapsing

The collapsed state is uncontrolled by default. Use defaultCollapsed for the initial state, or isCollapsed together with onCollapsedChange to control it — for example to persist the user's preference, or to collapse the panel from elsewhere in the app.

Acme Cloud
/notes
Example
SideNavPanelApp.tsx
import {SideNavPanel, SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, Collection, Text} from '@react-spectrum/s2/SideNavPanel';
import {SideNavPanelApp} from './SideNavPanelApp';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import File from '@react-spectrum/s2/icons/File';
import Files from '@react-spectrum/s2/icons/Files';
import Folder from '@react-spectrum/s2/icons/Folder';
import Images from '@react-spectrum/s2/icons/Images';
import {useState} from 'react';

function CollapsibleSideNavPanel() { let [isCollapsed, setCollapsed] = useState(true); return ( <SideNavPanelApp defaultSelectedRoute="/notes" isCollapsed={isCollapsed} onCollapsedChange={setCollapsed}> {({selectedRoute}) => ( <SideNavPanel isCollapsed={isCollapsed} onCollapsedChange={setCollapsed} styles={style({gridArea: 'sidebar', width: 208, marginX: 8})}> <SideNav aria-label="Files" selectedRoute={selectedRoute} items={items} styles={style({width: 'full'})}> {function renderItem(item) { let Icon = item.icon ?? (item.type === 'directory' ? Folder : File); return ( <SideNavItem textValue={item.title} href={item.href}> <SideNavItemContent> <SideNavItemLink> <Icon /> <Text>{item.title}</Text> </SideNavItemLink> </SideNavItemContent> {item.children && <Collection items={item.children}> {renderItem} </Collection>} </SideNavItem> ); }} </SideNav> </SideNavPanel> )} </SideNavPanelApp> ); } <CollapsibleSideNavPanel />

Animations

SideNavPanel and SideNav animate themselves. The panel's width animates first, and its contents animate only once it has settled.

Because the contents wait for the width, the isCollapsed you read from SideNavPanelContext trails the panel itself while it is expanding.

AccountFooter in the example below reads isCollapsed from SideNavPanelContext and hides its name with display: none while the panel is collapsed.

Context valueDescription
isCollapsedWhether the panel's contents are collapsed. This trails the panel itself while it is expanding, since its contents are only brought back once it has finished widening.
setCollapsedCollapses or expands the panel. Useful for building your own expand and collapse trigger.
Acme Cloud
Your account
/notes
Example
SideNavPanelApp.tsx
import {SideNavPanel, SideNav, SideNavItem, SideNavItemContent, SideNavItemLink, Collection, Text} from '@react-spectrum/s2/SideNavPanel';
import {SideNavPanelApp, AccountFooter} from './SideNavPanelApp';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import File from '@react-spectrum/s2/icons/File';
import Files from '@react-spectrum/s2/icons/Files';
import Images from '@react-spectrum/s2/icons/Images';
import Archive from '@react-spectrum/s2/icons/Archive';
import Folder from '@react-spectrum/s2/icons/Folder';

<SideNavPanelApp defaultSelectedRoute="/notes"> {({selectedRoute}) => ( <SideNavPanel defaultCollapsed styles={style({gridArea: 'sidebar', width: 208, marginX: 8})}> <SideNav aria-label="Files" selectedRoute={selectedRoute} items={items} styles={style({width: 'full'})}> {function renderItem(item) { let Icon = item.icon ?? (item.type === 'directory' ? Folder : File); return ( <SideNavItem textValue={item.title} href={item.href}> <SideNavItemContent> <SideNavItemLink> <Icon /> <Text>{item.title}</Text> </SideNavItemLink> </SideNavItemContent> {item.children && <Collection items={item.children}> {renderItem} </Collection>} </SideNavItem>
); }} </SideNav> <AccountFooter /> </SideNavPanel> )} </SideNavPanelApp>

API

<SideNavPanel>
  <SideNav>
    <SideNavItem href>
      <SideNavItemContent>
        <SideNavItemLink>
          <Icon />
          <Text />
        </SideNavItemLink>
      </SideNavItemContent>
    </SideNavItem>
  </SideNav>
</SideNavPanel>

SideNavPanel Props

NameType
childrenReactNode

The content of the side panel.

isCollapsedboolean

Whether the side panel is collapsed (controlled).

defaultCollapsedboolean

Whether the side panel is collapsed by default (uncontrolled).

styles

Spectrum-defined styles, returned by the style() macro.