A component to display a navigation sidebar.

Importing

The component can be imported via:

import { SideNav, SideNavGroup } from '@customerio/pluma-components/react';

Usage

SideNav is a vertical navigation component that contains NavItem, optionally wrapped in groups. The SideNav can be collapsed and expanded.

<Box display="flex" style={{ height: 400 }}>
  <SideNav>
    <SideNavGroup title="Manage">
      <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
      <NavItem href="#" icon="object">Objects</NavItem>
    </SideNavGroup>
    <SideNavGroup title="Create">
      <NavItemExpandable defaultIsOpen={true}>
        <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Message Library</NavItem>
          <NavItem href="#">Assets</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
  </SideNav>
</Box>

Default Collapsed State

Use defaultIsCollapsed to render the side navigation in a collapsed state initially. The user can still expand and collapse it using the toggle button.

<Box display="flex" style={{ height: 400 }}>
  <SideNav defaultIsCollapsed={true}>
    <SideNavGroup title="Manage">
      <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
      <NavItemExpandable>
        <NavItemExpandableTrigger icon="database">Data & Integrations</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Data Index</NavItem>
          <NavItem href="#">Integrations</NavItem>
          <NavItem href="#">Imports</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
    <SideNavGroup title="Create">
      <NavItemExpandable defaultIsOpen={true}>
        <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Message Library</NavItem>
          <NavItem href="#">Assets</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
  </SideNav>
</Box>

Controlled Collapse State

For full control over the collapse state, use isCollapsed together with onCollapseChange.

import { useState } from 'react';
import { Box,
  SideNav,
  SideNavGroup,
  NavItem,
  NavItemExpandable,
  NavItemExpandableTrigger,
  NavItemExpandableContent,
} from '@customerio/pluma-components/react';

export default function Example() {
  const [isCollapsed, setIsCollapsed] = useState(false);

  return (
    <Box display="flex" style={{ height: 400 }}>
      <SideNav isCollapsed={isCollapsed} onCollapseChange={setIsCollapsed}>
        <SideNavGroup title="Manage">
          <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
          <NavItemExpandable>
            <NavItemExpandableTrigger icon="database">Data & Integrations</NavItemExpandableTrigger>
            <NavItemExpandableContent>
              <NavItem href="#">Data Index</NavItem>
              <NavItem href="#">Integrations</NavItem>
              <NavItem href="#">Imports</NavItem>
            </NavItemExpandableContent>
          </NavItemExpandable>
        </SideNavGroup>
        <SideNavGroup title="Create">
          <NavItemExpandable defaultIsOpen={true}>
            <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
            <NavItemExpandableContent>
              <NavItem href="#">Message Library</NavItem>
              <NavItem href="#">Assets</NavItem>
            </NavItemExpandableContent>
          </NavItemExpandable>
        </SideNavGroup>
      </SideNav>
    </Box>
  );
}

Not Collapsible

Set isCollapsible to false to hide the collapse/expand toggle button.

<Box display="flex" style={{ height: 400 }}>
  <SideNav isCollapsible={false}>
    <SideNavGroup title="Manage">
      <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
      <NavItem href="#" icon="object">Objects</NavItem>
    </SideNavGroup>
    <SideNavGroup title="Create">
      <NavItemExpandable defaultIsOpen={true}>
        <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Message Library</NavItem>
          <NavItem href="#">Assets</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
  </SideNav>
</Box>

Groups

Use SideNavGroup to organize nav items into labeled sections. The title prop is optional — groups without a title still provide visual spacing between sections.

<Box display="flex" style={{ height: 650 }}>
  <SideNav>
    <SideNavGroup title="Manage">
      <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
      <NavItemExpandable>
        <NavItemExpandableTrigger icon="database">Data & Integrations</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Data Index</NavItem>
          <NavItem href="#">Integrations</NavItem>
          <NavItem href="#">Imports</NavItem>
          <NavItem href="#">Exports</NavItem>
          <NavItem href="#">Collections</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
    <SideNavGroup title="Create">
      <NavItemExpandable defaultIsOpen={true}>
        <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
        <NavItemExpandableContent>
          <NavItem href="#">Message Library</NavItem>
          <NavItem href="#">Assets</NavItem>
          <NavItem href="#">Email Layouts</NavItem>
          <NavItem href="#">Snippets</NavItem>
        </NavItemExpandableContent>
      </NavItemExpandable>
    </SideNavGroup>
    <SideNavGroup>
      <NavItem href="#" icon="manage">Settings</NavItem>
    </SideNavGroup>
  </SideNav>
</Box>

Provider

SideNavProvider manages the side navigation's collapse state at the top of your layout, making it accessible to any nested component via context. This is useful when components outside the SideNav itself need to read or control the collapse state.

When a SideNav is rendered inside a SideNavProvider, it automatically inherits the provider's state instead of managing its own.

The provider accepts the same state props as SideNav: isCollapsed, onCollapseChange, defaultIsCollapsed, and isCollapsible.

import {
  SideNavProvider,
  useSideNavState,
  useSetSideNavState,
  useToggleSideNav,
} from '@customerio/pluma-components/react';

Usage

In this example, the SideNavProvider wraps the entire layout. A button in the main content area uses useToggleSideNav to control the side navigation without needing direct access to the SideNav component.

Side nav is: expanded

import {
  Box,
  Stack, InlineStack,
  Button,
  NavItem,
  NavItemExpandable,
  NavItemExpandableContent,
  NavItemExpandableTrigger,
  Paragraph,
  SideNav,
  SideNavGroup,
  SideNavProvider,
  useSideNavState,
  useToggleSideNav,
} from '@customerio/pluma-components/react';

function ToggleContent() {
  const toggleSideNav = useToggleSideNav();

  return (
    <Box>
      <Button onClick={toggleSideNav}>
        Toggle side nav
      </Button>
    </Box>
  );
}

function StateContent() {
  const state = useSideNavState();
  return (
    <Box>
      <Paragraph>
        Side nav is: {state.isCollapsed ? 'collapsed' : 'expanded'}
      </Paragraph>
    </Box>
  );
}

export default function Example() {
  return (
    <SideNavProvider>
      <InlineStack alignBlock="stretch" style={{ height: 400 }}>
        <SideNav>
          <SideNavGroup title="Manage">
            <NavItem href="#" icon="person" isSelected={true}>People</NavItem>
            <NavItem href="#" icon="object">Objects</NavItem>
          </SideNavGroup>
          <SideNavGroup title="Create">
            <NavItemExpandable defaultIsOpen={true}>
              <NavItemExpandableTrigger icon="layout">Content</NavItemExpandableTrigger>
              <NavItemExpandableContent>
                <NavItem href="#">Message Library</NavItem>
                <NavItem href="#">Assets</NavItem>
              </NavItemExpandableContent>
            </NavItemExpandable>
          </SideNavGroup>
        </SideNav>

        <Stack p="200" gap="200">
          <ToggleContent />
          <StateContent />
        </Stack>
      </InlineStack>
    </SideNavProvider>
  );
}

API

Whether the side navigation is collapsed by default - use for uncontrolled collapse state when you want the side navigation to be in a specific state initially.

Whether the side navigation is collapsed. Use for controlled collapse state.

Default:true

Whether the side navigation can be collapsed and expanded. When false, the collapse/expand toggle button is hidden.

A callback function that is called when the collapse state changes. Use in combination with isCollapsed for controlled collapse state.

Whether the side navigation is collapsed by default - use for uncontrolled collapse state when you want the side navigation to be in a specific state initially.

Whether the side navigation is collapsed. Use for controlled collapse state.

Default:true

Whether the side navigation can be collapsed and expanded. When false, the collapse/expand toggle button is hidden.

A callback function that is called when the collapse state changes. Use in combination with isCollapsed for controlled collapse state.

An optional title for the group, which can be used to provide context or categorization for the items within.