SideNavigation is start-aligned and arranged vertically. It is used to navigate between page urls or sections when you have too many menu items to fit in horizontal Tabs.

also known as Legend

Figma:

Responsive:

Adaptive:

A11y:

Props

Component props
Name
Type
Default
accessibilityLabel
Required
string
-

String that clients such as VoiceOver will read to describe the element.

children
Required
React.Node
-

The content shown in SideNavigation. See subcomponents.

collapsed
boolean
-

When passed SideNavigation becomes a collapsible controlled component. If not passed, it stays non-collapsible. See the collapsible variant to learn more. This functionality is not supported in mobile.

dismissButton
{ accessibilityLabel?: string, onDismiss: () => void }
-

Callback fired when SideNavigation requests to be closed in mobile devices. Must be used to control SideNavigation´s on/off display state. The accessibilityLabel should follow the Accessibility guidelines.

React.Node
-

Content to display at the bottom of SideNavigation. Open slot available to display other functionality required in the page. See the Footer variant to learn more.

header
React.Node
-

Content to display at the top of SideNavigation. Open slot used for controlling the display of navigation items. See the Header variant to learn more.

mobileTitle
string
-

Title for mobile navigation.

onCollapse
(boolean) => void
-

Callback fired when the SideNavigation is collapsible and the collapse/expand button is clicked. This functionality is not supported in mobile.

onPreview
(boolean) => void
-

Callback fired when the collapsed SideNavigation is hovered and opened in preview mode. This functionality is not supported in mobile.

showBorder
boolean
-

Displays a border in SideNavigation. See the Border variant for more info.

Usage guidelines

When to use
  • 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
When not to use
  • When you only have two to five items to navigate through. Use Tabs instead
  • When you need to open a menu of external links and actions via a button. Use Dropdown instead

Best practices

Do

Keep item labels brief and clear to keep them memorable and scannable.

Don't

Use long text labels that end up wrapping, especially in certain languages. Don’t shorten labels so much that they are hard to understand.

Do

Group related items and use section headings to help users parse information and help with redundancy.

Don't

Provide an unordered “kitchen sink” of features that is hard to parse and creates redundancy.

Do

Use icons that clearly match their text labels and serve as bullet points for the content.

Don't

Use icons if you’ll be forced to include icons that don’t quite reinforce their text labels.

Do

Place under the PageHeader when SideNav is used to navigate urls that are sub-sections of a main page

Don't

Omit a PageHeader and make it seem like each SideNav item is a primary, independent page.

Accessibility

Active item

SideNavigation.TopItem and SideNavigation.NestedItem have an "active" state that visually identifies it. To set them 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 all text strings. Note that localization can lengthen text by 20 to 30 percent.

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.

Note that dismissButton.accessibilityLabel is optional as DefaultLabelProvider provides default strings. Use custom labels if they need to be more specific.

SideNavigation.Section depends on DefaultLabelProvider for internal text strings. Localize the texts via DefaultLabelProvider. Learn more
import React from 'react';
import { DefaultLabelProvider, SideNavigation } from 'gestalt';

export default function Example() {
  return (
    <DefaultLabelProvider
      // $FlowExpectedError[incompatible-type] For demostration purposes
      labels={{
        SideNavigation: {
          accessibilityDismissButtonLabel: 'Seitennavigation verwerfen',
        },
      }}
    >
      <SideNavigation accessibilityLabel="Beispiel für Lokalisierung">
        <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: 'Neu', type: 'info' }}
        />
      </SideNavigation>
    </DefaultLabelProvider>
  );
}

Subcomponents

SideNavigation.Section

Use SideNavigation.Section to categorize navigation menu items into groups and also avoid redundant language in labels.

SideNavigation.Section Props

SideNavigation.Section subcomponent props
Name
Type
Default
children
Required
React.Node
-

Any SideNavigation.TopItem to be rendered

label
Required
string
-

Label for the section. See the Sections variant for more info.

SideNavigation.TopItem

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.

SideNavigation.TopItem Props

SideNavigation.TopItem subcomponent props
Name
Type
Default
href
Required
string
-

Directs users to the url when item is selected.

label
Required
string
-

Label for the item.

active
"page" | "section"
-

When set to 'page' or 'section', it displays the item in "active" state. See the Accessibility guidelines to learn more.

badge
{
  text: string,
  type?: "info" | "error" | "warning" | "success" | "neutral",
}
-

When supplied, will display a Badge next to the item's label. See the Badges variant to learn more.

counter
{ number: string, accessibilityLabel: string }
-

When supplied, will display a counter. See the Counter variant to learn more.

icon
$Keys<typeof icons> | { __path: string }
-

When supplied, will display Icon. See the Icon variant to learn more.

notificationAccessibilityLabel
string
-

When supplied, will display a notification dot. See the Notification variant to learn more.

onClick
({
  event: SyntheticMouseEvent<HTMLAnchorElement> | SyntheticKeyboardEvent<HTMLAnchorElement>,
  dangerouslyDisableOnNavigation: () => void,
}) => void
-

Callback when the user selects an item using the mouse or keyboard.

primaryAction
{
  icon?: "ellipsis" | "edit" | "trash-can",
  onClick?: ({
    event: SyntheticMouseEvent<HTMLDivElement> | SyntheticKeyboardEvent<HTMLDivElement>,
  }) => void,
  tooltip: {
    accessibilityLabel?: string,
    text: string,
    zIndex?: Indexable,
  },
  dropdownItems?: $ReadOnlyArray<React.Element<typeof Dropdown.Item>>,
}
-

The primary action for each item. See the primary action variant to learn more.

ref
HTMLLIElement
-

Ref that is forwarded to the underlying li element.

SideNavigation.NestedItem

Use SideNavigation.NestedItem to redirect the user to a different page or section. SideNavigation.NestedItem must be used in second and third nested levels.

SideNavigation.NestedItem Props

SideNavigation.NestedItem subcomponent props
Name
Type
Default
href
Required
string
-

Directs users to the url when item is selected.

label
Required
string
-

Label for the item.

active
"page" | "section"
-

When set to 'page' or 'section', it displays the item in "active" state. See the Accessibility guidelines to learn more.

counter
{ number: string, accessibilityLabel: string }
-

When supplied, will display a counter. See the Counter variant to learn more.

onClick
({
  event: SyntheticMouseEvent<HTMLAnchorElement> | SyntheticKeyboardEvent<HTMLAnchorElement>,
  dangerouslyDisableOnNavigation: () => void,
}) => void
-

Callback when the user selects an item using the mouse or keyboard.

ref
HTMLLIElement
-

Ref that is forwarded to the underlying li element.

SideNavigation.Group

Use SideNavigation.Group to hold SideNavigation.NestedItem and SideNavigation.NestedGroup at the top level of SideNavigation. It supports badges, icons, counters, and notifications.

SideNavigation.Group Props

SideNavigation.Group subcomponent props
Name
Type
Default
children
Required
React.Node
-

Content of the group. See nested directory variant for more information.

label
Required
string
-

Label for the group. See nested directory variant for more information.

badge
{
  text: string,
  type?: "info" | "error" | "warning" | "success" | "neutral",
}
-

When supplied, will display a Badge next to the item's label. See the Badges variant to learn more.

counter
{ number: string, accessibilityLabel: string }
-

When supplied, will display a counter. See the Counter variant to learn more.

display
"expandable" | "static"
"expandable"

Nested directories can be static or expandable. See the group display variant to learn more.

expanded
boolean
-

When passed SideNavigation.Group becomes a controlled component. If not passed, it stays uncontrolled. See the controlled group display variant to learn more. This functionality is not supported in mobile.

icon
$Keys<typeof icons> | { __path: string }
-

When supplied, will display Icon. See the Icon variant to learn more.

notificationAccessibilityLabel
string
-

When supplied, will display a notification dot. See the Notification variant to learn more.

onExpand
({ expanded: boolean }) => void
-

Callback fired when the expand button component is clicked and the component is controlled.This functionality is not supported in mobile.

primaryAction
{
  icon?: "ellipsis" | "edit" | "trash-can",
  onClick?: ({
    event: SyntheticMouseEvent<HTMLDivElement> | SyntheticKeyboardEvent<HTMLDivElement>,
  }) => void,
  tooltip: {
    accessibilityLabel?: string,
    text: string,
    zIndex?: Indexable,
  },
  dropdownItems?: $ReadOnlyArray<React.Element<typeof Dropdown.Item>>,
}
-

The primary action for each group. See the primary action variant to learn more.

SideNavigation.NestedGroup

Use SideNavigation.NestedGroup to hold SideNavigation.NestedItem in the second nested level of Pageheader.

SideNavigation.NestedGroup Props

SideNavigation.NestedGroup subcomponent props
Name
Type
Default
children
Required
React.Node
-

Content of the group. See nested directory variant for more information.

label
Required
string
-

Label for the group. See nested directory variant for more information.

counter
{ number: string, accessibilityLabel: string }
-

When supplied, will display a counter. See the Counter variant to learn more.

display
"expandable" | "static"
"expandable"

Nested directories can be static or expandable. See the group display variant to learn more.

expanded
boolean
-

When passed SideNavigation.Group becomes a controlled component. If not passed, it stays uncontrolled. See the controlled group display variant to learn more. This functionality is not supported in mobile.

onExpand
({ expanded: boolean }) => void
-

Callback fired when the expand button component is clicked and the component is controlled. This functionality is not supported in mobile.

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>
  );
}

Headers allow for sorting filters or another UI control to be included at the top of the navigation.

import { Fragment, useState } from 'react';
import { Flex, RadioGroup, SideNavigation } from 'gestalt';

export default function Example() {
  const [organisedBy, setOrganisedBy] = 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' ? (
        <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>
        </Fragment>
      ) : (
        <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" />
        </Fragment>
      )}
    </SideNavigation>
  );
}

Footers allow for filters, additional external links or other UI controls to be included at the bottom of the navigation.

import { Box, Fieldset, Flex, SideNavigation, Text } from 'gestalt';
import { DatePicker } from 'gestalt-datepicker';

export default function Example() {
  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box width="100%" height="100%">
        <Box height="100%" width={280} overflow="scroll">
          <SideNavigation
            accessibilityLabel="Footer example"
            footer={
              <Flex direction="column" gap={{ column: 4, row: 0 }}>
                <Text size="300" weight="bold">
                  Filters
                </Text>
                <Fieldset legend="Campaign filters" legendDisplay="hidden">
                  <Flex direction="column" gap={{ column: 4, row: 0 }}>
                    <DatePicker
                      id="example-start-date"
                      label="Start"
                      onChange={() => {}}
                      rangeSelector="start"
                      value={new Date()}
                    />
                    <DatePicker
                      id="example-end-date"
                      label="End"
                      onChange={() => {}}
                      rangeSelector="end"
                      value={new Date(+7)}
                    />
                  </Flex>
                </Fieldset>
              </Flex>
            }
          >
            <SideNavigation.Section label="Campaigns">
              {[
                {
                  label: 'Active',
                  counter: { number: '200', accessibilityLabel: '200 Pins' },
                },
                {
                  label: 'Draft',
                  counter: { number: '100', accessibilityLabel: '100 Pins' },
                },
              ].map(({ label, counter }) => (
                <SideNavigation.TopItem
                  key={label}
                  href="#"
                  label={label}
                  icon="ads-stats"
                  counter={counter}
                />
              ))}
            </SideNavigation.Section>
          </SideNavigation>
        </Box>
      </Box>
    </Box>
  );
}

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.

Gestalt icon
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>
  );
}

Custom icon
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 the information is useful for the user to know before clicking on a menu item. To prevent visual overload, do not include counters in the parent if the children have counters.

import React from 'react';
import { SideNavigation } from 'gestalt';

export default function Example() {
  return (
    <SideNavigation accessibilityLabel="Counters example">
      <SideNavigation.Section label="Ads">
        <SideNavigation.TopItem
          href="#"
          onClick={({ event }) => event.preventDefault()}
          label="Campaigns"
          counter={{ number: '2', accessibilityLabel: 'You have 2 campaigns' }}
        />
        <SideNavigation.Group label="Catalogues">
          <SideNavigation.NestedGroup label="Christmas">
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Data sources"
              counter={{
                number: '20',
                accessibilityLabel: 'You have 20 data sources',
              }}
            />
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Product groups"
              counter={{
                number: '5',
                accessibilityLabel: 'You have 5 product groups',
              }}
            />
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Listings"
              counter={{
                number: '200',
                accessibilityLabel: 'You have 200 listings',
              }}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedGroup label="Thanksgiving">
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Data sources"
              counter={{
                number: '15',
                accessibilityLabel: 'You have 15 data sources',
              }}
            />
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Product groups"
              counter={{
                number: '3',
                accessibilityLabel: 'You have 3 product groups',
              }}
            />
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="Listings"
              counter={{
                number: '100',
                accessibilityLabel: 'You have 100 listings',
              }}
            />
          </SideNavigation.NestedGroup>
        </SideNavigation.Group>
      </SideNavigation.Section>
    </SideNavigation>
  );
}

Primary action

SideNavigation.TopItem and SideNavigation.Group support an optional primaryAction.

primaryAction can be a simple IconButton or a Dropdown. For the latter, set dropdownItems using an array of Dropdown.Item.

import { useState } from 'react';
import {
  Box,
  Button,
  DeviceTypeProvider,
  Dropdown,
  Flex,
  SideNavigation,
} from 'gestalt';

export default function Example() {
  const [mobile, setMobile] = useState(false);

  return (
    <Box color="lightWash" padding={4} height="100%">
      <Flex gap={12}>
        <Button
          text="View mobile"
          onClick={() => setMobile(true)}
          color="red"
        />
        <DeviceTypeProvider deviceType={mobile ? 'mobile' : 'desktop'}>
          <Box
            position={mobile ? 'absolute' : undefined}
            top
            bottom
            left
            right
            id="sidenavigation"
          >
            <SideNavigation
              accessibilityLabel="Notification example"
              dismissButton={{ onDismiss: () => setMobile((value) => !value) }}
            >
              <SideNavigation.TopItem
                href="#"
                onClick={({ event }) => {
                  event.preventDefault();
                }}
                label="Campaign z-168i"
                primaryAction={{
                  icon: 'edit',
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'Rename campaign' },
                }}
              />
              <SideNavigation.TopItem
                href="#"
                onClick={({ event }) => {
                  event.preventDefault();
                }}
                label="Campaign a-j6ki (inactive)"
                primaryAction={{
                  icon: 'trash-can',
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'Delete campaign' },
                }}
              />
              <SideNavigation.Group
                label="Campaign drafts"
                counter={{
                  number: '12',
                  accessibilityLabel: '12 campaign drafts',
                }}
                primaryAction={{
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'More options' },
                  dropdownItems: [
                    <Dropdown.Item
                      key="edit"
                      option={{ value: 'Edit', label: 'Edit' }}
                      onSelect={() => {}}
                    />,
                    <Dropdown.Item
                      key="trash"
                      option={{ value: 'Delete', label: 'Delete' }}
                      onSelect={() => {}}
                    />,
                  ],
                }}
              >
                <SideNavigation.NestedItem
                  counter={{
                    number: '10',
                    accessibilityLabel: 'You have 10 messages in your inbox',
                  }}
                  href="#"
                  onClick={({ event }) => event.preventDefault()}
                  label="West Coast"
                />
                <SideNavigation.NestedItem
                  href="#"
                  onClick={({ event }) => event.preventDefault()}
                  label="East Coast"
                />
              </SideNavigation.Group>
              <SideNavigation.TopItem
                href="#"
                onClick={({ event }) => event.preventDefault()}
                label="Archived campaigns"
                primaryAction={{
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'More options' },
                  dropdownItems: [
                    <Dropdown.Item
                      key="edit"
                      option={{ value: 'Edit', label: 'Edit' }}
                      onSelect={() => {}}
                    />,
                    <Dropdown.Item
                      key="trash"
                      option={{ value: 'Delete', label: 'Delete' }}
                      onSelect={() => {}}
                    />,
                  ],
                }}
                counter={{
                  number: '87',
                  accessibilityLabel: '87 archived campaings',
                }}
              />
            </SideNavigation>
          </Box>
        </DeviceTypeProvider>
      </Flex>
    </Box>
  );
}

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>
  );
}

Group display

Group display can be:

  1. static: Group is expanded by default and there isn't expanding/collapsing behavior
  2. expandable: Group is expandable and all items are initially collapsed except groups with active children.

When display='expandable', we can manage the state of each group's collapsing/expanded state by setting SideNavigation.Group and SideNavigation.NestedGroup as controlled components.

To work as controlled components, set expand prop to a boolean value. If not passed or set to "undefined", they stay uncontrolled.

Beware that when controlled, the list path to the active item is not automatically expanded.

display='static'
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.Group label="Christmas" icon="people" display="static">
          <SideNavigation.NestedItem
            href="#"
            onClick={({ event }) => event.preventDefault()}
            label="Luxury Christmas"
          />
          <SideNavigation.NestedGroup
            label="Classic Christmas"
            display="static"
          >
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="West Coast"
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            onClick={({ event }) => event.preventDefault()}
            label="Luxury Christmas"
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

display='expandable'
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.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.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            onClick={({ event }) => event.preventDefault()}
            label="Luxury Christmas"
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

Controlled group display
import { useState } from 'react';
import { Box, SideNavigation } from 'gestalt';

export default function Example() {
  const [expandedElements, setExpandedElements] = useState([
    'Christmas',
    'Classic Christmas',
  ]);

  const handleOnExpand =
    (name) =>
    ({ expanded }) => {
      if (expanded) {
        setExpandedElements([...expandedElements, name]);
      } else {
        setExpandedElements(expandedElements.filter((value) => value !== name));
      }
    };

  return (
    <Box height={362} width={280} overflow="scroll">
      <SideNavigation accessibilityLabel="Nested items example">
        <SideNavigation.Group
          label="Christmas"
          icon="people"
          display="expandable"
          expanded={expandedElements.includes('Christmas')}
          onExpand={handleOnExpand('Christmas')}
        >
          <SideNavigation.NestedItem
            href="#"
            onClick={({ event }) => event.preventDefault()}
            label="Luxury Christmas"
          />
          <SideNavigation.NestedGroup
            label="Classic Christmas"
            display="expandable"
            expanded={expandedElements.includes('Classic Christmas')}
            onExpand={handleOnExpand('Classic Christmas')}
          >
            <SideNavigation.NestedItem
              href="#"
              onClick={({ event }) => event.preventDefault()}
              label="West Coast"
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            onClick={({ event }) => event.preventDefault()}
            label="Luxury Christmas"
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

Subcomponent composability

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>
  );
}

Collapsible

Providing collapsed prop to SideNavigation makes the component collapsible. Collapsible variant of SideNavigation is a controlled component and it has expand/collapse icon button at the top. Clicking the icon button reveals a complete list of navigation options when expanded and minimizes these options, often to a series of compact icons or completely hidden when collapsed. This variant is not available for mobile.

Items with icons

When collapsed, if all the navigation items have icons, SideNavigation is displayed as a compact set of icons.

import React, { useState } from 'react';
import { Box, Heading, SideNavigation } from 'gestalt';

export default function Example() {
  const [page, setPage] = useState('');
  const [collapsed, setCollapsed] = useState(false);

  return (
    <Box width={1000} height="100%" display="flex" overflow="scroll">
      <Box maxWidth={280}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          showBorder
          collapsed={collapsed}
          onCollapse={setCollapsed}
        >
          <SideNavigation.TopItem
            href="#"
            label="Trends"
            icon="trending"
            active={page === '1' ? 'page' : undefined}
            notificationAccessibilityLabel="New data available"
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
          />

          <SideNavigation.TopItem
            href="#"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
            label="Business Details"
            icon="business-hierarchy"
            active={page === '2' ? 'page' : undefined}
            badge={{ text: 'New', type: 'success' }}
            counter={{ number: '10', accessibilityLabel: 'New details' }}
          />

          <SideNavigation.Section label="Public Holidays">
            <SideNavigation.Group label="Christmas" icon="people">
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '3' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('3');
                }}
              />
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '4' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('4');
                }}
              />
              <SideNavigation.NestedGroup label="Classic Christmas">
                <SideNavigation.NestedItem
                  href="#"
                  label="West Coast"
                  active={page === '5' ? 'page' : undefined}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage('5');
                  }}
                />
              </SideNavigation.NestedGroup>
            </SideNavigation.Group>

            <SideNavigation.TopItem
              href="#"
              label="Public profile"
              active={page === '6' ? 'page' : undefined}
              icon="overview"
              notificationAccessibilityLabel="Needs your attention"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('6');
              }}
            />

            <SideNavigation.TopItem
              href="#"
              label="Personal information"
              active={page === '7' ? 'page' : undefined}
              icon="workflow-status-unstarted"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('7');
              }}
            />
          </SideNavigation.Section>

          <SideNavigation.Section label="Contacts">
            <SideNavigation.TopItem
              href="#"
              label="Contact Information"
              icon="phone"
              active={page === '8' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('8');
              }}
            />
            <SideNavigation.TopItem
              href="#"
              label="Other Details"
              icon="history"
              active={page === '9' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('9');
              }}
            />
          </SideNavigation.Section>
        </SideNavigation>
      </Box>

      <Box padding={4}>
        <Heading size="500">Page main content</Heading>
      </Box>
    </Box>
  );
}

Mixed items with and without icons

If some of the navigation items don't have icons, those items are collapsed into an ellipsis. Clicking on ellipses expands the SideNavigation in preview mode and when an item is selected, the component is collapsed again.

import React, { useState } from 'react';
import { Box, Heading, SideNavigation } from 'gestalt';

export default function Example() {
  const [page, setPage] = useState('');
  const [collapsed, setCollapsed] = useState(false);

  return (
    <Box width={1000} height="100%" display="flex" overflow="scroll">
      <Box maxWidth={280}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          showBorder
          collapsed={collapsed}
          onCollapse={setCollapsed}
        >
          <SideNavigation.TopItem
            href="#"
            label="Trends"
            icon="trending"
            active={page === '1' ? 'page' : undefined}
            notificationAccessibilityLabel="New data available"
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
          />

          <SideNavigation.TopItem
            href="#"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
            label="Business Details"
            icon="business-hierarchy"
            active={page === '2' ? 'page' : undefined}
            badge={{ text: 'New', type: 'success' }}
            counter={{ number: '10', accessibilityLabel: 'New details' }}
          />

          <SideNavigation.Section label="Public Holidays">
            <SideNavigation.Group label="Christmas">
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '3' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('3');
                }}
              />
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '4' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('4');
                }}
              />
              <SideNavigation.NestedGroup label="Classic Christmas">
                <SideNavigation.NestedItem
                  href="#"
                  label="West Coast"
                  active={page === '5' ? 'page' : undefined}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage('5');
                  }}
                />
              </SideNavigation.NestedGroup>
            </SideNavigation.Group>

            <SideNavigation.TopItem
              href="#"
              label="Public profile"
              active={page === '6' ? 'page' : undefined}
              icon="overview"
              notificationAccessibilityLabel="Needs your attention"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('6');
              }}
            />

            <SideNavigation.TopItem
              href="#"
              label="Personal information"
              active={page === '7' ? 'page' : undefined}
              icon="workflow-status-unstarted"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('7');
              }}
            />
          </SideNavigation.Section>

          <SideNavigation.Section label="Contacts">
            <SideNavigation.TopItem
              href="#"
              label="Contact Information"
              icon="phone"
              active={page === '8' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('8');
              }}
            />
            <SideNavigation.TopItem
              href="#"
              label="Other Details"
              active={page === '9' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('9');
              }}
            />
          </SideNavigation.Section>
        </SideNavigation>
      </Box>

      <Box padding={4}>
        <Heading size="500">Page main content</Heading>
      </Box>
    </Box>
  );
}

Collapsible header

When collapsed, the header and the footer of SideNavigation should be rendered accordingly.

import React, { useState } from 'react';
import { Box, Flex, Heading, Icon, SideNavigation } from 'gestalt';

export default function Example() {
  const [page, setPage] = useState('');
  const [collapsed, setCollapsed] = useState(false);
  const [preview, setPreview] = useState(false);

  return (
    <Box width={1000} height="100%" display="flex" overflow="scroll">
      <Box maxWidth={280}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          showBorder
          collapsed={collapsed}
          onCollapse={setCollapsed}
          onPreview={setPreview}
          header={
            collapsed && !preview ? (
              <Flex gap={2} alignItems="center" justifyContent="center">
                <Flex.Item>
                  <Icon
                    icon="pinterest"
                    size={36}
                    accessibilityLabel=""
                    color="brandPrimary"
                  />
                </Flex.Item>
              </Flex>
            ) : (
              <Flex gap={2} alignItems="center">
                <Flex.Item>
                  <Icon
                    icon="pinterest"
                    size={36}
                    accessibilityLabel=""
                    color="brandPrimary"
                  />
                </Flex.Item>
                <Flex.Item>
                  <Heading size="400">Pinterest Business</Heading>
                </Flex.Item>
              </Flex>
            )
          }
        >
          <SideNavigation.TopItem
            href="#"
            label="Trends"
            icon="trending"
            active={page === '1' ? 'page' : undefined}
            notificationAccessibilityLabel="New data available"
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
          />

          <SideNavigation.TopItem
            href="#"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
            label="Business Details"
            icon="business-hierarchy"
            active={page === '2' ? 'page' : undefined}
            badge={{ text: 'New', type: 'success' }}
            counter={{ number: '10', accessibilityLabel: 'New details' }}
          />

          <SideNavigation.Section label="Contacts">
            <SideNavigation.TopItem
              href="#"
              label="Contact Information"
              icon="phone"
              active={page === '8' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('8');
              }}
            />
            <SideNavigation.TopItem
              href="#"
              label="Other Details"
              icon="history"
              active={page === '9' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('9');
              }}
            />
          </SideNavigation.Section>
        </SideNavigation>
      </Box>

      <Box padding={4}>
        <Heading size="500">Page main content</Heading>
      </Box>
    </Box>
  );
}

Items without icons

If none of the SideNavigation items have icons, the component is collapsed only with expand icon button.

import React, { useState } from 'react';
import { Box, Heading, SideNavigation } from 'gestalt';

export default function Example() {
  const [page, setPage] = useState('');
  const [collapsed, setCollapsed] = useState(false);

  return (
    <Box width={1000} height="100%" display="flex" overflow="scroll">
      <Box maxWidth={280}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          showBorder
          collapsed={collapsed}
          onCollapse={setCollapsed}
        >
          <SideNavigation.TopItem
            href="#"
            label="Trends"
            active={page === '1' ? 'page' : undefined}
            notificationAccessibilityLabel="New data available"
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
          />

          <SideNavigation.TopItem
            href="#"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
            label="Business Details"
            active={page === '2' ? 'page' : undefined}
            badge={{ text: 'New', type: 'success' }}
            counter={{ number: '10', accessibilityLabel: 'New details' }}
          />

          <SideNavigation.Section label="Public Holidays">
            <SideNavigation.Group label="Christmas">
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '3' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('3');
                }}
              />
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                active={page === '4' ? 'page' : undefined}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage('4');
                }}
              />
              <SideNavigation.NestedGroup label="Classic Christmas">
                <SideNavigation.NestedItem
                  href="#"
                  label="West Coast"
                  active={page === '5' ? 'page' : undefined}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage('5');
                  }}
                />
              </SideNavigation.NestedGroup>
            </SideNavigation.Group>

            <SideNavigation.TopItem
              href="#"
              label="Public profile"
              active={page === '6' ? 'page' : undefined}
              notificationAccessibilityLabel="Needs your attention"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('6');
              }}
            />

            <SideNavigation.TopItem
              href="#"
              label="Personal information"
              active={page === '7' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('7');
              }}
            />
          </SideNavigation.Section>

          <SideNavigation.Section label="Contacts">
            <SideNavigation.TopItem
              href="#"
              label="Contact Information"
              active={page === '8' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('8');
              }}
            />
            <SideNavigation.TopItem
              href="#"
              label="Other Details"
              active={page === '9' ? 'page' : undefined}
              onClick={({ event }) => {
                event.preventDefault();
                setPage('9');
              }}
            />
          </SideNavigation.Section>
        </SideNavigation>
      </Box>

      <Box padding={4}>
        <Heading size="500">Page main content</Heading>
      </Box>
    </Box>
  );
}

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 { useState } from 'react';
import { Box, Button, DeviceTypeProvider, SideNavigation } from 'gestalt';

export default function Example() {
  const [showNav, setShowNav] = useState(false);

  return showNav ? (
    <DeviceTypeProvider deviceType="mobile">
      <Box position="absolute" top bottom left right id="sidenavigation">
        <SideNavigation
          mobileTitle="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

Do
  • Keep menu item labels brief, remembering that length is language-dependent
Don't
  • 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

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

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.