SideNavigation Pilot
Props
Usage guidelines
- When Tabs or a top navigation cannot accommodate the number of links or sections you need to navigate through
- When a deep hierarchy of navigation items is needed
Best practices
Keep item labels brief and clear to keep them memorable and scannable.
Use long text labels that end up wrapping, especially in certain languages. Don’t shorten labels so much that they are hard to understand.
Group related items and use section headings to help users parse information and help with redundancy.
Provide an unordered “kitchen sink” of features that is hard to parse and creates redundancy.
Use icons that clearly match their text labels and serve as bullet points for the content.
Use icons if you’ll be forced to include icons that don’t quite reinforce their text labels.
Place under the PageHeader when SideNav is used to navigate urls that are sub-sections of a main page
Omit a PageHeader and make it seem like each SideNav item is a primary, independent page.
Accessibility
Active item
SideNavigation.TopItem has an "active" state that visually identifies it. To set SideNavigation.TopItem to "active" set 'active="page"' (page redirect) or 'active="section"'. Use routing hooks from React.Router or other frameworks to identify the current route. For example, if the current pathname matches the SideNavigation.TopItem href, set SideNavigation.TopItem to "page". Use the example below as a reference.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { const reactRouterPath = '/sidenavigation'; return ( <SideNavigation accessibilityLabel="Active item example"> <SideNavigation.Section label="Navigation"> <SideNavigation.TopItem href="#" label="PageHeader" /> <SideNavigation.TopItem href="#" label="Tabs" /> <SideNavigation.TopItem active={reactRouterPath === '/sidenavigation' ? 'page' : undefined} href="#" label="SideNavigation" badge={{ text: 'New', type: 'info' }} /> </SideNavigation.Section> <SideNavigation.Section label="Controls"> <SideNavigation.TopItem href="#" label="RadioButton" badge={{ text: 'Deprecated', type: 'warning' }} /> <SideNavigation.TopItem href="#" label="RadioGroup" /> </SideNavigation.Section> </SideNavigation> ); }
Localization
Be sure to localize the accessibilityLabel
in SideNavigation and all subcomponents as well. When the text of the SideNav.Item becomes longer than the width of the menu, either intentionally or through localization, will wrap as needed to display the full text. Keep this in mind when selecting wording for your SideNavigation menu items.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Localization example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="bell" label="Benachrichtigungen" counter={{ number: '20', accessibilityLabel: 'Sie haben 20 Benachrichtigungen', }} notificationAccessibilityLabel="Du hast neue Benachrichtigungen" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="speech" label="Mitteilungen" counter={{ number: '10', accessibilityLabel: 'Sie haben 10 Nachrichten', }} notificationAccessibilityLabel="Sie haben neue Nachrichten" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="cog" label="Einstellungen" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="lock" label="Geschäftszugriff" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="add-layout" label="Optimieren Sie Ihren Home-Feed" badge={{ text: 'New', type: 'info' }} /> </SideNavigation> ); }
Subcomponents
Use SideNavigation.Section to categorize navigation menu items into groups and also avoid redundant language in labels.
Use SideNavigation.TopItem to redirect the user to a different page or section. SideNavigation.TopItem must be used at the top level of SideNavigation. It supports badges, icons, counters, and notifications.
Use SideNavigation.NestedItem to redirect the user to a different page or section. SideNavigation.NestedItem must be used in second and third nested levels.
Use SideNavigation.Group to hold SideNavigation.NestedItem and SideNavigation.NestedGroup at the top level of SideNavigation. It supports badges, icons, counters, and notifications.
Use SideNavigation.NestedGroup to hold SideNavigation.NestedItem in the second nested level of Pageheader.
Variants
Sections
Sections help with categorizing navigation menu items into groups and also avoid redundant language in labels.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Sections example"> <SideNavigation.Section label="Resources"> <SideNavigation.TopItem href="#" label="ESLint plugin" /> <SideNavigation.TopItem href="#" label="FAQ" /> <SideNavigation.TopItem href="#" label="How to hack around Gestalt" /> <SideNavigation.TopItem href="#" label="Tooling" /> </SideNavigation.Section> <SideNavigation.Section label="Foundations"> <SideNavigation.TopItem href="#" label="Accessibility" /> <SideNavigation.TopItem href="#" label="Elevation" /> <SideNavigation.TopItem href="#" label="Typography" /> <SideNavigation.TopItem href="#" label="Color palette" /> </SideNavigation.Section> </SideNavigation> ); }
Header
Headers allow for sorting filters or another UI control to be included at the top of the navigation.
import React from 'react'; import { SideNavigation, RadioGroup, Flex } from 'gestalt'; export default function Example() { const [organisedBy, setOrganisedBy] = React.useState('categorized'); return ( <SideNavigation accessibilityLabel="Header example" header={ <RadioGroup legend="Sort by?" id="example-header"> <Flex gap={{ row: 2, column: 0, }} > <RadioGroup.RadioButton checked={organisedBy === 'categorized'} id="category" label="Category" name="SortCategory" onChange={() => setOrganisedBy('categorized')} value="categorized" size="sm" /> <RadioGroup.RadioButton checked={organisedBy === 'alphabetical'} id="alphabetical" label="Alphabetical" name="SortCAlphabetical" onChange={() => setOrganisedBy('alphabetical')} value="alphabetical" size="sm" /> </Flex> </RadioGroup> } > {organisedBy === 'categorized' ? ( <React.Fragment> <SideNavigation.Section label="Navigation"> <SideNavigation.TopItem href="#" label="PageHeader" /> <SideNavigation.TopItem href="#" label="Tabs" /> <SideNavigation.TopItem href="#" label="SideNavigation" badge={{ text: 'New', type: 'info' }} /> </SideNavigation.Section> <SideNavigation.Section label="Controls"> <SideNavigation.TopItem href="#" label="RadioButton" badge={{ text: 'Deprecated', type: 'warning' }} /> <SideNavigation.TopItem href="#" label="RadioGroup" /> </SideNavigation.Section> </React.Fragment> ) : ( <React.Fragment> <SideNavigation.TopItem href="#" label="PageHeader" /> <SideNavigation.TopItem href="#" label="RadioButton" badge={{ text: 'Deprecated', type: 'warning' }} /> <SideNavigation.TopItem href="#" label="RadioGroup" /> <SideNavigation.TopItem href="#" label="SideNavigation" badge={{ text: 'New', type: 'info' }} /> <SideNavigation.TopItem href="/web/tabs" label="Tabs" /> </React.Fragment> )} </SideNavigation> ); }
Footers allow for filters, additional external links or other UI controls to be included at the bottom of the navigation.
Badge
A badge can be added to a menu label with information that may be useful to a person, such as whether a page is new or is a beta or deprecated feature. Only supported in SideNavigation.TopItem and SideNavigation.Group.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Badge example"> <SideNavigation.Section label="Navigation"> <SideNavigation.TopItem href="#" label="PageHeader" /> <SideNavigation.TopItem href="#" label="Tabs" /> <SideNavigation.TopItem href="#" label="SideNavigation" badge={{ text: 'New', type: 'info' }} /> </SideNavigation.Section> <SideNavigation.Section label="Controls"> <SideNavigation.TopItem href="#" label="RadioButton" badge={{ text: 'Deprecated', type: 'warning' }} /> <SideNavigation.TopItem href="#" label="RadioGroup" /> </SideNavigation.Section> </SideNavigation> ); }
Border
A border can be added to the end edge of the navigation on dense surfaces with tight spacing where it helps to visually separate the nav from other content.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Border example" showBorder> <SideNavigation.Section label="Navigation"> <SideNavigation.TopItem href="#" label="PageHeader" /> <SideNavigation.TopItem href="#" label="Tabs" /> <SideNavigation.TopItem href="#" label="SideNavigation" /> </SideNavigation.Section> <SideNavigation.Section label="Controls"> <SideNavigation.TopItem href="#" label="RadioButton" /> <SideNavigation.TopItem href="#" label="RadioGroup" /> </SideNavigation.Section> </SideNavigation> ); }
Icons
Icons are used when simple, clear icons help users with scanning the content of a menu. Only supported in SideNavigation.TopItem and SideNavigation.Group.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Icons example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="bell" label="Notifications" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="speech" label="Messages" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="cog" label="Settings" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="lock" label="Business Access" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon="add-layout" label="Tune your home feed" /> </SideNavigation> ); }
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Custom icons example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon={{ __path: 'M14 17.5c0 1.378-1.122 2.5-2.5 2.5A2.503 2.503 0 0 1 9 17.5V17h5v.5zm8.947-1.87L18.701 2.712a1.022 1.022 0 0 0-1.566-.521l-15.7 11.24c-.37.264-.525.744-.382 1.179l.551 1.678c.14.425.532.712.974.712H7v.5a4.5 4.5 0 0 0 9 0V17h5.973c.7 0 1.195-.696.974-1.37z', }} label="Notifications" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon={{ __path: 'M0 6a4 4 0 0 1 4-4h16a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4zm3.52-.88 7.53 6.16a1.5 1.5 0 0 0 1.9 0l7.53-6.16A1 1 0 0 0 20 5H4a1 1 0 0 0-.48.12zM3 8.57V18a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V8.57l-6.15 5.04a4.5 4.5 0 0 1-5.7 0z', }} label="Messages" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon={{ __path: 'm2.337 19.942 5.671-1.977L19.265 6.706c.981-.98.981-2.57 0-3.55l-1.42-1.421a2.51 2.51 0 0 0-3.55 0L3.036 12.992l-1.978 5.671a1.005 1.005 0 0 0 1.279 1.279M23 22c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1s.45-1 1-1h20c.55 0 1 .45 1 1', }} label="Settings" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon={{ __path: 'M23 5v14a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4v-5.5h10.258l-1.94 1.939a1.5 1.5 0 0 0 2.121 2.122L17 12l-5.561-5.561a1.501 1.501 0 0 0-2.121 2.122l1.94 1.939H1V5a4 4 0 0 1 4-4h14a4 4 0 0 1 4 4', }} label="Business Access" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} icon={{ __path: 'M5 1h5.75v22H5c-2.2 0-4-1.8-4-4V5c0-2.2 1.8-4 4-4zm18 4v5.75h-9.75V1H19c2.2 0 4 1.8 4 4zm-9.75 8.25H23V19c0 2.2-1.8 4-4 4h-5.75z', }} label="Tune your home feed" /> </SideNavigation> ); }
Notification
A red indicator dot can be added to signify new items on a page or things that need your attention. Only supported in SideNavigation.TopItem and SideNavigation.Group.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Notification example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Notifications" counter={{ number: '20', accessibilityLabel: 'You have 20 notifications in your inbox', }} notificationAccessibilityLabel="New notifications" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Messages" counter={{ number: '10', accessibilityLabel: 'You have 10 messages in your inbox', }} notificationAccessibilityLabel="New messages" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Settings" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Business Access" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Tune your home feed" /> </SideNavigation> ); }
Counters
Counters can be included as indicators of the number of items on a page or section. Only include counters if it’s information that’s useful to the user to know before clicking on a menu item. Only supported in SideNavigation.TopItem and SideNavigation.Group.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { return ( <SideNavigation accessibilityLabel="Counters example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Under review" counter={{ number: '20', accessibilityLabel: 'You have 20 Idea Pins under review', }} /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Drafts" counter={{ number: '5', accessibilityLabel: 'You have 5 Idea Pins drafts', }} /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Published" counter={{ number: '200', accessibilityLabel: 'You have published 200 Idea Pins', }} /> </SideNavigation> ); }
Nested directory
SideNavigation supports three navigation levels. The top level is composed of SideNavigation.TopItem and SideNavigation.Group. The second nested level is composed of SideNavigation.NestedGroup and SideNavigation.Item. The third nested level is composed of SideNavigation.Item
import React from 'react'; import { Box, SideNavigation } from 'gestalt'; export default function Example() { return ( <Box height={362} width={280} overflow="scroll"> <SideNavigation accessibilityLabel="Nested items example"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Reporting" icon="ads-stats" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Conversions" icon="replace" /> <SideNavigation.Section label="Audiences"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Thanksgiving" icon="people" /> <SideNavigation.Group label="Christmas" icon="people"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="Luxury Christmas" /> <SideNavigation.NestedGroup label="Classic Christmas"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> </SideNavigation.NestedGroup> <SideNavigation.NestedGroup label="Alternative Christmas"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" active="section" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> </SideNavigation.NestedGroup> </SideNavigation.Group> <SideNavigation.Group label="Halloween" icon="people" display="static" > <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> </SideNavigation.Group> </SideNavigation.Section> </SideNavigation> </Box> ); }
Subcomponent composability
Under the hood, SideNavigation recognizes subcomponents by display name. SideNavigation requires its own subcomponents as children to build the list of navigation items.
When building SideNavigation, we might want to render different combinations of subcomponents conditionally. SideNavigation supports simple conditional rendering of subcomponents lists wrapped in React.Fragment as well as consecutive arrays of subcomponent arrays. See the example below which illustrates both of these cases. More logic complexity might break the correct SideNavigation behavior.
import React from 'react'; import { SideNavigation } from 'gestalt'; export default function Example() { const someCondition = true; return ( <SideNavigation accessibilityLabel="Subcomponent composability example"> {someCondition && ( <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Trends" icon="ads-stats" /> )} <SideNavigation.Section label="Analytics"> {someCondition && ( <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Reporting" icon="ads-stats" /> )} {someCondition && ( <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Conversions" icon="replace" /> )} </SideNavigation.Section> <SideNavigation.Section label="Audiences"> <SideNavigation.Group label="Christmas" icon="people"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="Luxury Christmas" /> <SideNavigation.NestedGroup label="Classic Christmas"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> </SideNavigation.NestedGroup> <SideNavigation.NestedGroup label="Alternative Christmas"> {['West Coast', 'East Coast'].map((x) => ( <SideNavigation.NestedItem href="#" key={`xmas${x}`} onClick={({ event }) => event.preventDefault()} label={x} /> ))} {['Southern', 'NorthEast'].map((x) => ( <SideNavigation.NestedItem href="#" key={`xmas${x}`} onClick={({ event }) => event.preventDefault()} label={x} /> ))} </SideNavigation.NestedGroup> </SideNavigation.Group> <SideNavigation.Group label="Halloween" icon="people"> {['West Coast', 'East Coast'].map((x) => ( <SideNavigation.NestedItem href="#" key={`halloween${x}`} onClick={({ event }) => event.preventDefault()} label={x} /> ))} </SideNavigation.Group> </SideNavigation.Section> </SideNavigation> ); }
Mobile
SideNavigation requires DeviceTypeProvider to enable its mobile user interface. The example below shows the mobile platform UI and its implementation.
For mobile, title
and dismissButton
become required props.
Notice that the mobile UI requires logic to hide and show SideNavigation full width. If Button or TapArea control the visibility of SideNavigation, use accessibilityControls
so that screen reader users can identify the relationship between elements.
import React from 'react'; import { Box, SideNavigation, DeviceTypeProvider, Button } from 'gestalt'; export default function Example() { const [showNav, setShowNav] = React.useState(false); return showNav ? ( <DeviceTypeProvider deviceType="mobile"> <Box position="absolute" top bottom left right id="sidenavigation"> <SideNavigation title="Advertisement" accessibilityLabel="Mobile device example" dismissButton={{ onDismiss: () => setShowNav(false), accessibilityLabel: 'Close navigation', }} > <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Reporting" icon="ads-stats" /> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Conversions" icon="replace" /> <SideNavigation.Section label="Audiences"> <SideNavigation.TopItem href="#" onClick={({ event }) => event.preventDefault()} label="Thanksgiving" icon="people" /> <SideNavigation.Group label="Christmas" icon="people"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="Luxury Christmas" /> <SideNavigation.NestedGroup label="Classic Christmas"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> </SideNavigation.NestedGroup> <SideNavigation.NestedGroup label="Alternative Christmas"> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> </SideNavigation.NestedGroup> </SideNavigation.Group> <SideNavigation.Group label="Halloween" icon="people" display="static" > <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="East Coast" /> <SideNavigation.NestedItem href="#" onClick={({ event }) => event.preventDefault()} label="West Coast" /> </SideNavigation.Group> </SideNavigation.Section> </SideNavigation> </Box> </DeviceTypeProvider> ) : ( <Box padding={2}> <Button accessibilityLabel="Show navigation" accessibilityControls="sidenavigation" color="red" text="Show navigation" size="lg" onClick={() => setShowNav(true)} /> </Box> ); }
Writing
- Keep menu item labels brief, remembering that length is language-dependent
- Use complete sentences or lengthy descriptions
- Be redundant; use a section header if you find yourself repeating the same word over and over again in labels
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Component is not currently available in Figma. | |
Responsive Web | Ready | Component is available in code for web and mobile web. |
iOS | Component is not currently available in code for iOS. | |
Android | Component is not currently available in code for Android. |
Related
Tabs
Used to navigate between urls and placed horizontally. Tabs can be used when there are 2–5 URLs to navigate between and can be used as a sub-navigation after the top nav bar or after a SideNav.
SegmentedControl
SegmentedControl is used to select between different views or arrangements of related content. Like Tabs, this control is horizontal, but unlike tabs, it doesn’t navigated between URLs.
Dropdown
A custom menu to select URLs to navigate to, or actions to take. This is always triggered by a button.
PageHeader
For pages with a main top nav bar, every SideNav should have a PageHeader to announce the main page that the navigation items belong to. Exceptions are internal tools or developer platform interfaces where the SideNav is the main navigation.