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:

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
(arg1: boolean) => void
-

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

onPreview
(arg1: 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}
          badge={{ text: 'New', type: 'info' }}
          href="#"
          label="SideNavigation"
        />
      </SideNavigation.Section>
      <SideNavigation.Section label="Controls">
        <SideNavigation.TopItem
          badge={{ text: 'Deprecated', type: 'warning' }}
          href="#"
          label="RadioButton"
        />
        <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
      labels={{
        SideNavigation: {
          accessibilityDismissButtonLabel: 'Seitennavigation verwerfen',
        },
      }}
    >
      <SideNavigation accessibilityLabel="Beispiel für Lokalisierung">
        <SideNavigation.TopItem
          counter={{
            number: '20',
            accessibilityLabel: 'Sie haben 20 Benachrichtigungen',
          }}
          href="#"
          icon="bell"
          label="Benachrichtigungen"
          notificationAccessibilityLabel="Du hast neue Benachrichtigungen"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.TopItem
          counter={{
            number: '10',
            accessibilityLabel: 'Sie haben 10 Nachrichten',
          }}
          href="#"
          icon="speech"
          label="Mitteilungen"
          notificationAccessibilityLabel="Sie haben neue Nachrichten"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.TopItem
          href="#"
          icon="cog"
          label="Einstellungen"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.TopItem
          href="#"
          icon="lock"
          label="Geschäftszugriff"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.TopItem
          badge={{ text: 'Neu', type: 'info' }}
          href="#"
          icon="add-layout"
          label="Optimieren Sie Ihren Home-Feed"
          onClick={({ event }) => event.preventDefault()}
        />
      </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.

disabled
boolean
-

Indicates if row item is disabled and therefore inactive and non-interactive. See disabled variant to learn more.

icon
keyof 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
(arg1: {
  event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
  dangerouslyDisableOnNavigation: () => void;
}) => void
-

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

primaryAction
{
  icon?: "ellipsis" | "edit" | "trash-can";
  onClick?: (arg1: {
    event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
  }) => void;
  tooltip: {
    accessibilityLabel?: string;
    text: string;
    zIndex?: Indexable;
  };
  dropdownItems?: ReadonlyArray<ReactElement>;
}
-

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.

subtext
string
-

Provides optional additional information about a row item. See subtext variant to learn more.

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.

disabled
boolean
-

Indicates if row item is disabled and therefore inactive and non-interactive. See disabled variant to learn more.

onClick
(arg1: {
  event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
  dangerouslyDisableOnNavigation: () => void;
}) => void
-

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

primaryAction
{
  icon?: "ellipsis" | "edit" | "trash-can";
  onClick?: (arg1: {
    event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
  }) => void;
  tooltip: {
    accessibilityLabel?: string;
    text: string;
    zIndex?: Indexable;
  };
  dropdownItems?: ReadonlyArray<ReactElement>;
}
-

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.

subtext
string
-

Provides optional additional information about a row item. See subtext variant to learn more.

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.

active
"page" | "section"
-

When set to 'page' or 'section', it displays the item in "active" state. Available only when href prop is present. 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.

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.

href
string
-

Directs users to the url when item is selected. Available only for Desktop.

icon
keyof 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
(arg1: {
  event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
  dangerouslyDisableOnNavigation: () => void;
}) => void
-

Callback when the user selects an item using the mouse or keyboard. Available only when href prop is present.

onExpand
(arg1: { 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?: (arg1: {
    event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
  }) => void;
  tooltip: {
    accessibilityLabel?: string;
    text: string;
    zIndex?: Indexable;
  };
  dropdownItems?: ReadonlyArray<ReactElement>;
}
-

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.

active
"page" | "section"
-

When set to 'page' or 'section', it displays the item in "active" state. Available only when href prop is present. 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.

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.

href
string
-

Directs users to the url when item is selected. Available only for Desktop.

onClick
(arg1: {
  event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
  dangerouslyDisableOnNavigation: () => void;
}) => void
-

Callback when the user selects an item using the mouse or keyboard. Available only when href prop is present.

onExpand
(arg1: { 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?: (arg1: {
    event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
  }) => void;
  tooltip: {
    accessibilityLabel?: string;
    text: string;
    zIndex?: Indexable;
  };
  dropdownItems?: ReadonlyArray<ReactElement>;
}
-

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

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 id="example-header" legend="Sort by?">
          <Flex
            gap={{
              row: 2,
              column: 0,
            }}
          >
            <RadioGroup.RadioButton
              checked={organisedBy === 'categorized'}
              id="category"
              label="Category"
              name="SortCategory"
              onChange={() => setOrganisedBy('categorized')}
              size="sm"
              value="categorized"
            />
            <RadioGroup.RadioButton
              checked={organisedBy === 'alphabetical'}
              id="alphabetical"
              label="Alphabetical"
              name="SortCAlphabetical"
              onChange={() => setOrganisedBy('alphabetical')}
              size="sm"
              value="alphabetical"
            />
          </Flex>
        </RadioGroup>
      }
    >
      {organisedBy === 'categorized' ? (
        <Fragment>
          <SideNavigation.Section label="Navigation">
            <SideNavigation.TopItem href="#" label="PageHeader" />
            <SideNavigation.TopItem href="#" label="Tabs" />
            <SideNavigation.TopItem
              badge={{ text: 'New', type: 'info' }}
              href="#"
              label="SideNavigation"
            />
          </SideNavigation.Section>
          <SideNavigation.Section label="Controls">
            <SideNavigation.TopItem
              badge={{ text: 'Deprecated', type: 'warning' }}
              href="#"
              label="RadioButton"
            />
            <SideNavigation.TopItem href="#" label="RadioGroup" />
          </SideNavigation.Section>
        </Fragment>
      ) : (
        <Fragment>
          <SideNavigation.TopItem href="#" label="PageHeader" />
          <SideNavigation.TopItem
            badge={{ text: 'Deprecated', type: 'warning' }}
            href="#"
            label="RadioButton"
          />
          <SideNavigation.TopItem href="#" label="RadioGroup" />
          <SideNavigation.TopItem
            badge={{ text: 'New', type: 'info' }}
            href="#"
            label="SideNavigation"
          />
          <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 height="100%" padding={2} width={300}>
      <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}
              counter={counter}
              href="#"
              icon="ads-stats"
              label={label}
            />
          ))}
        </SideNavigation.Section>
      </SideNavigation>
    </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
          badge={{ text: 'New', type: 'info' }}
          href="#"
          label="SideNavigation"
        />
      </SideNavigation.Section>
      <SideNavigation.Section label="Controls">
        <SideNavigation.TopItem
          badge={{ text: 'Deprecated', type: 'warning' }}
          href="#"
          label="RadioButton"
        />
        <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>
  );
}

Disabled

Items can be disabled to prevent clicking or navigation to a page if that page is unavailable to users.

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

export default function Example() {
  return (
    <Box height="100%" width={300}>
      <SideNavigation accessibilityLabel="Disabled item example">
        <SideNavigation.TopItem
          disabled
          href="#"
          icon="ads-stats"
          label="Reports"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.Section label="Catalogs">
          <SideNavigation.TopItem
            disabled
            href="#"
            icon="people"
            label="Thanksgiving"
            onClick={({ event }) => event.preventDefault()}
            subtext="Disabled thanksgiving section"
          />

          <SideNavigation.Group expanded icon="people" label="Christmas">
            <SideNavigation.NestedItem
              disabled
              href="#"
              label="Luxury Christmas"
              onClick={({ event }) => event.preventDefault()}
            />

            <SideNavigation.NestedGroup label="Shopping Categories">
              <SideNavigation.NestedItem
                disabled
                href="#"
                label="Gifts"
                onClick={({ event }) => event.preventDefault()}
              />
              <SideNavigation.NestedItem
                href="#"
                label="Decorations"
                onClick={({ event }) => event.preventDefault()}
              />
            </SideNavigation.NestedGroup>
          </SideNavigation.Group>
        </SideNavigation.Section>
      </SideNavigation>
    </Box>
  );
}

Subtext

Subtext adds a new line to an item, providing additional information about the page the item represents.

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

export default function Example() {
  return (
    <Box height="100%" width={300}>
      <SideNavigation accessibilityLabel="Helper text items">
        <SideNavigation.TopItem
          href="#"
          icon="ads-stats"
          label="Reports"
          onClick={({ event }) => event.preventDefault()}
          subtext="Reporting subtext"
        />
        <SideNavigation.Section label="Audiences">
          <SideNavigation.TopItem
            href="#"
            icon="people"
            label="Group A"
            onClick={({ event }) => event.preventDefault()}
            subtext="8.5m+ users"
          />

          <SideNavigation.Group expanded icon="people" label="Group B">
            <SideNavigation.NestedItem
              href="#"
              label="Sub audience 1"
              onClick={({ event }) => event.preventDefault()}
              subtext="2.3m+ users"
            />

            <SideNavigation.NestedGroup label="Sub audience 2">
              <SideNavigation.NestedItem
                href="#"
                label="Region 1"
                onClick={({ event }) => event.preventDefault()}
                subtext="100k+ users"
              />
              <SideNavigation.NestedItem
                href="#"
                label="Region 2"
                onClick={({ event }) => event.preventDefault()}
                subtext="50k+ users"
              />
            </SideNavigation.NestedGroup>
          </SideNavigation.Group>
        </SideNavigation.Section>
      </SideNavigation>
    </Box>
  );
}

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="#"
        icon="bell"
        label="Notifications"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        icon="speech"
        label="Messages"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        icon="cog"
        label="Settings"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        icon="lock"
        label="Business Access"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        icon="add-layout"
        label="Tune your home feed"
        onClick={({ event }) => event.preventDefault()}
      />
    </SideNavigation>
  );
}

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

export default function Example() {
  return (
    <SideNavigation accessibilityLabel="Custom icons example">
      <SideNavigation.TopItem
        href="#"
        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"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        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"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        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"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        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"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        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"
        onClick={({ event }) => event.preventDefault()}
      />
    </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
        counter={{
          number: '20',
          accessibilityLabel: 'You have 20 notifications in your inbox',
        }}
        href="#"
        label="Notifications"
        notificationAccessibilityLabel="New notifications"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        counter={{
          number: '10',
          accessibilityLabel: 'You have 10 messages in your inbox',
        }}
        href="#"
        label="Messages"
        notificationAccessibilityLabel="New messages"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        label="Settings"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        label="Business Access"
        onClick={({ event }) => event.preventDefault()}
      />
      <SideNavigation.TopItem
        href="#"
        label="Tune your home feed"
        onClick={({ event }) => event.preventDefault()}
      />
    </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
          counter={{ number: '2', accessibilityLabel: 'You have 2 campaigns' }}
          href="#"
          label="Campaigns"
          onClick={({ event }) => event.preventDefault()}
        />

        <SideNavigation.Group label="Catalogues">
          <SideNavigation.NestedGroup label="Christmas">
            <SideNavigation.NestedItem
              counter={{
                number: '20',
                accessibilityLabel: 'You have 20 data sources',
              }}
              href="#"
              label="Data sources"
              onClick={({ event }) => event.preventDefault()}
            />
            <SideNavigation.NestedItem
              counter={{
                number: '5',
                accessibilityLabel: 'You have 5 product groups',
              }}
              href="#"
              label="Product groups"
              onClick={({ event }) => event.preventDefault()}
            />
            <SideNavigation.NestedItem
              counter={{
                number: '200',
                accessibilityLabel: 'You have 200 listings',
              }}
              href="#"
              label="Listings"
              onClick={({ event }) => event.preventDefault()}
            />
          </SideNavigation.NestedGroup>

          <SideNavigation.NestedGroup label="Thanksgiving">
            <SideNavigation.NestedItem
              counter={{
                number: '15',
                accessibilityLabel: 'You have 15 data sources',
              }}
              href="#"
              label="Data sources"
              onClick={({ event }) => event.preventDefault()}
            />
            <SideNavigation.NestedItem
              counter={{
                number: '3',
                accessibilityLabel: 'You have 3 product groups',
              }}
              href="#"
              label="Product groups"
              onClick={({ event }) => event.preventDefault()}
            />
            <SideNavigation.NestedItem
              counter={{
                number: '100',
                accessibilityLabel: 'You have 100 listings',
              }}
              href="#"
              label="Listings"
              onClick={({ event }) => event.preventDefault()}
            />
          </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" height="100%" padding={4} position="relative">
      <Flex gap={12} justifyContent="center" wrap>
        <Button
          color="red"
          onClick={() => setMobile(true)}
          text="View mobile"
        />
        <DeviceTypeProvider deviceType={mobile ? 'mobile' : 'desktop'}>
          <Box
            bottom
            id="sidenavigation"
            left={mobile}
            position={mobile ? 'absolute' : undefined}
            right={mobile}
            top={mobile}
            width={mobile ? '100%' : 360}
          >
            <SideNavigation
              accessibilityLabel="Notification example"
              dismissButton={{ onDismiss: () => setMobile((value) => !value) }}
            >
              <SideNavigation.TopItem
                counter={{ number: '10', accessibilityLabel: '10 updates' }}
                href="#"
                label="Campaign z-168i"
                onClick={({ event }) => {
                  event.preventDefault();
                }}
                primaryAction={{
                  icon: 'edit',
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'Rename campaign' },
                }}
              />
              <SideNavigation.TopItem
                href="#"
                label="Campaign a-j6ki (inactive)"
                onClick={({ event }) => {
                  event.preventDefault();
                }}
                primaryAction={{
                  icon: 'trash-can',
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'Delete campaign' },
                }}
              />
              <SideNavigation.Group
                counter={{
                  number: '12',
                  accessibilityLabel: '12 campaign drafts',
                }}
                label="Campaign drafts"
                primaryAction={{
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'More options' },
                  dropdownItems: [
                    <Dropdown.Item
                      key="edit"
                      onSelect={() => {}}
                      option={{ value: 'Edit', label: 'Edit' }}
                    />,
                    <Dropdown.Item
                      key="trash"
                      onSelect={() => {}}
                      option={{ value: 'Delete', label: 'Delete' }}
                    />,
                  ],
                }}
              >
                <SideNavigation.NestedItem
                  counter={{
                    number: '10',
                    accessibilityLabel: 'You have 10 messages in your inbox',
                  }}
                  href="#"
                  label="West Coast"
                  onClick={({ event }) => event.preventDefault()}
                  primaryAction={{
                    onClick: ({ event }) => {
                      event.preventDefault();
                    },
                    tooltip: { text: 'More options' },
                    dropdownItems: [
                      <Dropdown.Item
                        key="edit"
                        onSelect={() => {}}
                        option={{ value: 'Edit', label: 'Edit' }}
                      />,
                      <Dropdown.Item
                        key="trash"
                        onSelect={() => {}}
                        option={{ value: 'Delete', label: 'Delete' }}
                      />,
                    ],
                  }}
                />
                <SideNavigation.NestedItem
                  href="#"
                  label="East Coast"
                  onClick={({ event }) => event.preventDefault()}
                  primaryAction={{
                    onClick: ({ event }) => {
                      event.preventDefault();
                    },
                    tooltip: { text: 'More options' },
                    dropdownItems: [
                      <Dropdown.Item
                        key="edit"
                        onSelect={() => {}}
                        option={{ value: 'Edit', label: 'Edit' }}
                      />,
                      <Dropdown.Item
                        key="trash"
                        onSelect={() => {}}
                        option={{ value: 'Delete', label: 'Delete' }}
                      />,
                    ],
                  }}
                />
                <SideNavigation.NestedGroup
                  label="Alternative campaigns"
                  primaryAction={{
                    onClick: ({ event }) => {
                      event.preventDefault();
                    },
                    tooltip: { text: 'More options' },
                    dropdownItems: [
                      <Dropdown.Item
                        key="edit"
                        onSelect={() => {}}
                        option={{ value: 'Edit', label: 'Edit' }}
                      />,
                      <Dropdown.Item
                        key="trash"
                        onSelect={() => {}}
                        option={{ value: 'Delete', label: 'Delete' }}
                      />,
                    ],
                  }}
                >
                  <SideNavigation.NestedItem
                    href="#"
                    label="West Coast"
                    onClick={({ event }) => event.preventDefault()}
                    primaryAction={{
                      onClick: ({ event }) => {
                        event.preventDefault();
                      },
                      tooltip: { text: 'More options' },
                      dropdownItems: [
                        <Dropdown.Item
                          key="edit"
                          onSelect={() => {}}
                          option={{ value: 'Edit', label: 'Edit' }}
                        />,
                        <Dropdown.Item
                          key="trash"
                          onSelect={() => {}}
                          option={{ value: 'Delete', label: 'Delete' }}
                        />,
                      ],
                    }}
                  />
                  <SideNavigation.NestedItem
                    href="#"
                    label="East Coast"
                    onClick={({ event }) => event.preventDefault()}
                  />
                </SideNavigation.NestedGroup>
              </SideNavigation.Group>
              <SideNavigation.TopItem
                counter={{
                  number: '87',
                  accessibilityLabel: '87 archived campaings',
                }}
                href="#"
                label="Archived campaigns"
                onClick={({ event }) => event.preventDefault()}
                primaryAction={{
                  onClick: ({ event }) => {
                    event.preventDefault();
                  },
                  tooltip: { text: 'More options' },
                  dropdownItems: [
                    <Dropdown.Item
                      key="edit"
                      onSelect={() => {}}
                      option={{ value: 'Edit', label: 'Edit' }}
                    />,
                    <Dropdown.Item
                      key="trash"
                      onSelect={() => {}}
                      option={{ value: 'Delete', label: 'Delete' }}
                    />,
                  ],
                }}
              />
            </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="100%" width={300}>
      <SideNavigation accessibilityLabel="Nested items example">
        <SideNavigation.TopItem
          href="#"
          icon="ads-stats"
          label="Reporting"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.TopItem
          href="#"
          icon="replace"
          label="Conversions"
          onClick={({ event }) => event.preventDefault()}
        />
        <SideNavigation.Section label="Audiences">
          <SideNavigation.TopItem
            href="#"
            icon="people"
            label="Thanksgiving"
            onClick={({ event }) => event.preventDefault()}
          />

          <SideNavigation.Group icon="people" label="Christmas">
            <SideNavigation.NestedItem
              href="#"
              label="Luxury Christmas"
              onClick={({ event }) => event.preventDefault()}
            />

            <SideNavigation.NestedGroup label="Classic Christmas">
              <SideNavigation.NestedItem
                href="#"
                label="West Coast"
                onClick={({ event }) => event.preventDefault()}
              />
              <SideNavigation.NestedItem
                href="#"
                label="East Coast"
                onClick={({ event }) => event.preventDefault()}
              />
            </SideNavigation.NestedGroup>

            <SideNavigation.NestedGroup label="Alternative Christmas">
              <SideNavigation.NestedItem
                active="section"
                href="#"
                label="West Coast"
                onClick={({ event }) => event.preventDefault()}
              />
              <SideNavigation.NestedItem
                href="#"
                label="East Coast"
                onClick={({ event }) => event.preventDefault()}
              />
            </SideNavigation.NestedGroup>
          </SideNavigation.Group>
          <SideNavigation.Group
            display="static"
            icon="people"
            label="Halloween"
          >
            <SideNavigation.NestedItem
              href="#"
              label="East Coast"
              onClick={({ event }) => event.preventDefault()}
            />
            <SideNavigation.NestedItem
              href="#"
              label="West Coast"
              onClick={({ event }) => event.preventDefault()}
            />
          </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="100%" width={280}>
      <SideNavigation accessibilityLabel="Nested items example" showBorder>
        <SideNavigation.Group display="static" icon="people" label="Christmas">
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />
          <SideNavigation.NestedGroup
            display="static"
            label="Classic Christmas"
          >
            <SideNavigation.NestedItem
              href="#"
              label="West Coast"
              onClick={({ event }) => event.preventDefault()}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

display='expandable'
import React from 'react';
import { Box, SideNavigation } from 'gestalt';

export default function Example() {
  return (
    <Box height="100%" width={280}>
      <SideNavigation accessibilityLabel="Nested items example" showBorder>
        <SideNavigation.Group icon="people" label="Christmas">
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />

          <SideNavigation.NestedGroup label="Classic Christmas">
            <SideNavigation.NestedItem
              href="#"
              label="West Coast"
              onClick={({ event }) => event.preventDefault()}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />
        </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="100%" width={280}>
      <SideNavigation accessibilityLabel="Nested items example" showBorder>
        <SideNavigation.Group
          display="expandable"
          expanded={expandedElements.includes('Christmas')}
          icon="people"
          label="Christmas"
          onExpand={handleOnExpand('Christmas')}
        >
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />
          <SideNavigation.NestedGroup
            display="expandable"
            expanded={expandedElements.includes('Classic Christmas')}
            label="Classic Christmas"
            onExpand={handleOnExpand('Classic Christmas')}
          >
            <SideNavigation.NestedItem
              href="#"
              label="West Coast"
              onClick={({ event }) => event.preventDefault()}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => event.preventDefault()}
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

Group items can also have links and navigate to a page.

display='static'
import React, { useState } from 'react';
import { Box, SideNavigation } from 'gestalt';

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

  return (
    <Box height="100%" width={280}>
      <SideNavigation accessibilityLabel="Nested items example" showBorder>
        <SideNavigation.Group
          active={page === '1' ? 'page' : undefined}
          display="static"
          href="#"
          icon="people"
          label="Christmas"
          onClick={({ event }) => {
            event.preventDefault();
            setPage('1');
          }}
        >
          <SideNavigation.NestedItem
            active={page === '2' ? 'page' : undefined}
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
          />
          <SideNavigation.NestedGroup
            active={page === '3' ? 'page' : undefined}
            display="static"
            href="#"
            label="Classic Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('3');
            }}
          >
            <SideNavigation.NestedItem
              active={page === '4' ? 'page' : undefined}
              href="#"
              label="West Coast"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('4');
              }}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            active={page === '5' ? 'page' : undefined}
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('5');
            }}
          />
        </SideNavigation.Group>
      </SideNavigation>
    </Box>
  );
}

display='expandable'
import React, { useState } from 'react';
import { Box, SideNavigation } from 'gestalt';

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

  return (
    <Box height="100%" width={280}>
      <SideNavigation accessibilityLabel="Nested items example" showBorder>
        <SideNavigation.Group
          active={page === '1' ? 'page' : undefined}
          href="#"
          icon="people"
          label="Christmas"
          onClick={({ event }) => {
            event.preventDefault();
            setPage('1');
          }}
        >
          <SideNavigation.NestedItem
            active={page === '2' ? 'page' : undefined}
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('2');
            }}
          />
          <SideNavigation.NestedGroup
            active={page === '3' ? 'page' : undefined}
            href="#"
            label="Classic Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('3');
            }}
          >
            <SideNavigation.NestedItem
              active={page === '4' ? 'page' : undefined}
              href="#"
              label="West Coast"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('4');
              }}
            />
          </SideNavigation.NestedGroup>
          <SideNavigation.NestedItem
            active={page === '5' ? 'page' : undefined}
            href="#"
            label="Luxury Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('5');
            }}
          />
        </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, { useState } from 'react';
import { Box, SideNavigation } from 'gestalt';

export default function Example() {
  const [page, setPage] = useState('');
  const someCondition = true;

  return (
    <Box height="100%" width={300}>
      <SideNavigation
        accessibilityLabel="Subcomponent composability example"
        showBorder
      >
        {someCondition && (
          <SideNavigation.TopItem
            active={page === 'Trends' ? 'page' : undefined}
            href="#"
            icon="ads-stats"
            label="Trends"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('Trends');
            }}
          />
        )}

        <SideNavigation.Section label="Analytics">
          {someCondition && (
            <SideNavigation.TopItem
              active={page === 'Reporting' ? 'page' : undefined}
              href="#"
              icon="ads-stats"
              label="Reporting"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('Reporting');
              }}
            />
          )}
          {someCondition && (
            <SideNavigation.TopItem
              active={page === 'Conversions' ? 'page' : undefined}
              href="#"
              icon="replace"
              label="Conversions"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('Conversions');
              }}
            />
          )}
        </SideNavigation.Section>

        <SideNavigation.Section label="Audiences">
          <SideNavigation.Group
            active={page === 'Christmas' ? 'page' : undefined}
            href="#"
            icon="people"
            label="Christmas"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('Christmas');
            }}
          >
            <SideNavigation.NestedItem
              active={page === 'Luxury Christmas' ? 'page' : undefined}
              href="#"
              label="Luxury Christmas"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('Luxury Christmas');
              }}
            />

            <SideNavigation.NestedGroup
              active={page === 'Classic Christmas' ? 'page' : undefined}
              href="#"
              label="Classic Christmas"
              onClick={({ event }) => {
                event.preventDefault();
                setPage('Classic Christmas');
              }}
            >
              {['West Coast', 'East Coast'].map((x) => (
                <SideNavigation.NestedItem
                  key={`xmas${x}`}
                  active={page === `Classic ${x}` ? 'page' : undefined}
                  href="#"
                  label={x}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage(`Classic ${x}`);
                  }}
                />
              ))}
            </SideNavigation.NestedGroup>

            <SideNavigation.NestedGroup label="Alternative Christmas">
              {['West Coast', 'East Coast'].map((x) => (
                <SideNavigation.NestedItem
                  key={`xmas${x}`}
                  active={page === `Alternative ${x}` ? 'page' : undefined}
                  href="#"
                  label={x}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage(`Alternative ${x}`);
                  }}
                />
              ))}
              {['Southern', 'NorthEast'].map((x) => (
                <SideNavigation.NestedItem
                  key={`xmas${x}`}
                  active={page === `Alternative ${x}` ? 'page' : undefined}
                  href="#"
                  label={x}
                  onClick={({ event }) => {
                    event.preventDefault();
                    setPage(`Alternative ${x}`);
                  }}
                />
              ))}
            </SideNavigation.NestedGroup>
          </SideNavigation.Group>

          <SideNavigation.Group icon="people" label="Halloween">
            {['West Coast', 'East Coast'].map((x) => (
              <SideNavigation.NestedItem
                key={`halloween${x}`}
                active={page === `Halloween ${x}` ? 'page' : undefined}
                href="#"
                label={x}
                onClick={({ event }) => {
                  event.preventDefault();
                  setPage(`Halloween ${x}`);
                }}
              />
            ))}
          </SideNavigation.Group>
        </SideNavigation.Section>
      </SideNavigation>
    </Box>
  );
}

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.

It is recommended the wrapper to be sticky (not fixed) so that it stays visible and keeps its position "relative" to other elements. Being sticky means SideNavigation's can naturally shift the adjacent elements/components.

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 display="flex" height="100%" overflow="auto">
      {/* It is recommended the wrapper to be sticky. */}
      <div style={{ position: 'sticky', top: 0 }}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          collapsed={collapsed}
          onCollapse={setCollapsed}
          showBorder
        >
          <SideNavigation.TopItem
            active={page === '1' ? 'page' : undefined}
            href="#"
            icon="trending"
            label="Trends"
            notificationAccessibilityLabel="New data available"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
          />

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

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

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

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

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

      <Box height={800} 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 display="flex" height="100%" overflow="auto">
      {/* It is recommended the wrapper to be sticky. */}
      <div style={{ position: 'sticky', top: 0 }}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          collapsed={collapsed}
          onCollapse={setCollapsed}
          showBorder
        >
          <SideNavigation.TopItem
            active={page === '1' ? 'page' : undefined}
            href="#"
            icon="trending"
            label="Trends"
            notificationAccessibilityLabel="New data available"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
          />

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

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

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

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

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

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

      <Box height={800} 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 display="flex" height="100%" overflow="auto">
      {/* It is recommended the wrapper to be sticky. */}
      <div style={{ position: 'sticky', top: 0 }}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          collapsed={collapsed}
          header={
            collapsed && !preview ? (
              <Flex alignItems="center" gap={2} justifyContent="center">
                <Flex.Item>
                  <Icon
                    accessibilityLabel=""
                    color="brandPrimary"
                    icon="pinterest"
                    size={36}
                  />
                </Flex.Item>
              </Flex>
            ) : (
              <Flex alignItems="center" gap={2}>
                <Flex.Item>
                  <Icon
                    accessibilityLabel=""
                    color="brandPrimary"
                    icon="pinterest"
                    size={36}
                  />
                </Flex.Item>
                <Flex.Item>
                  <Heading size="400">Pinterest Business</Heading>
                </Flex.Item>
              </Flex>
            )
          }
          onCollapse={setCollapsed}
          onPreview={setPreview}
          showBorder
        >
          <SideNavigation.TopItem
            active={page === '1' ? 'page' : undefined}
            href="#"
            icon="trending"
            label="Trends"
            notificationAccessibilityLabel="New data available"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
          />

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

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

      <Box height={800} 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 display="flex" height="100%" overflow="auto">
      {/* It is recommended the wrapper to be sticky. */}
      <div style={{ position: 'sticky', top: 0 }}>
        <SideNavigation
          accessibilityLabel="Collapsible example"
          collapsed={collapsed}
          onCollapse={setCollapsed}
          showBorder
        >
          <SideNavigation.TopItem
            active={page === '1' ? 'page' : undefined}
            href="#"
            label="Trends"
            notificationAccessibilityLabel="New data available"
            onClick={({ event }) => {
              event.preventDefault();
              setPage('1');
            }}
            primaryAction={{
              icon: 'ellipsis',
              tooltip: { text: 'Mark as read' },
            }}
          />

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

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

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

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

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

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

      <Box height={800} 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 bottom id="sidenavigation" left position="absolute" right top>
        <SideNavigation
          accessibilityLabel="Mobile device example"
          dismissButton={{
            onDismiss: () => setShowNav(false),
            accessibilityLabel: 'Close navigation',
          }}
          mobileTitle="Advertisement"
        >
          <SideNavigation.TopItem
            href="#"
            icon="ads-stats"
            label="Reporting"
            onClick={({ event }) => event.preventDefault()}
          />
          <SideNavigation.TopItem
            href="#"
            icon="replace"
            label="Conversions"
            onClick={({ event }) => event.preventDefault()}
          />
          <SideNavigation.Section label="Audiences">
            <SideNavigation.TopItem
              href="#"
              icon="people"
              label="Thanksgiving"
              onClick={({ event }) => event.preventDefault()}
            />

            <SideNavigation.Group icon="people" label="Christmas">
              <SideNavigation.NestedItem
                href="#"
                label="Luxury Christmas"
                onClick={({ event }) => event.preventDefault()}
              />

              <SideNavigation.NestedGroup label="Classic Christmas">
                <SideNavigation.NestedItem
                  href="#"
                  label="West Coast"
                  onClick={({ event }) => event.preventDefault()}
                />
                <SideNavigation.NestedItem
                  href="#"
                  label="East Coast"
                  onClick={({ event }) => event.preventDefault()}
                />
              </SideNavigation.NestedGroup>

              <SideNavigation.NestedGroup label="Alternative Christmas">
                <SideNavigation.NestedItem
                  href="#"
                  label="West Coast"
                  onClick={({ event }) => event.preventDefault()}
                />
                <SideNavigation.NestedItem
                  href="#"
                  label="East Coast"
                  onClick={({ event }) => event.preventDefault()}
                />
              </SideNavigation.NestedGroup>
            </SideNavigation.Group>
            <SideNavigation.Group
              display="static"
              icon="people"
              label="Halloween"
            >
              <SideNavigation.NestedItem
                href="#"
                label="East Coast"
                onClick={({ event }) => event.preventDefault()}
              />
              <SideNavigation.NestedItem
                href="#"
                label="West Coast"
                onClick={({ event }) => event.preventDefault()}
              />
            </SideNavigation.Group>
          </SideNavigation.Section>
        </SideNavigation>
      </Box>
    </DeviceTypeProvider>
  ) : (
    <Box padding={2}>
      <Button
        accessibilityControls="sidenavigation"
        accessibilityLabel="Show navigation"
        color="red"
        onClick={() => setShowNav(true)}
        size="lg"
        text="Show navigation"
      />
    </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.