SidebarItem

One entry of a Sidebar: an icon pinned to the anchored edge (it holds its position while the rail opens and closes) plus content shown while the rail is open - the label by default, or custom...

One entry of a Sidebar: an icon pinned to the anchored edge (it holds its position while the rail opens and closes) plus content shown while the rail is open - the label by default, or custom children (badges, counts, ...). The label is always the accessible name, so collapsed icon-only items stay screen-reader friendly. On a right-anchored rail the open content renders to the icon's left.

Built on Pressable, so it carries button semantics with only the styling declared here - no inherited uppercase, bold weight, or letter-spacing from the button family. It renders Pressable's default element, meaning role="button", a tab stop, and Enter/Space activation, with disabled expressed as aria-disabled. Selection styling and aria-pressed are its own; every Pressable prop (disabled, color, href, as, spacing, ...) passes through. Must render inside a Sidebar, which provides the open state and anchor via SidebarContext.

Usage

<SidebarItem
  icon="history"
  label="Gist history"
  isSelected={panel === 'gists'}
  onSelect={() => setPanel('gists')}
/>

// Custom open-state content - the label stays the accessible name:
<SidebarItem icon="edit_note" label="Drafts">
  <span>Drafts (3)</span>
</SidebarItem>

Under a Sidebar with hasPanel, children mean something different: they become the contents of the rail's panel column, shown while this item is selected, and the row falls back to the label. The item then also carries aria-expanded and, while selected, aria-controls pointing at the panel.

The children are ported into the panel via Portal, not handed upward - so they remain declared here and keep this item's context, state, and handlers, and the item may sit at any depth in the rail. Being ported, they are client-only: nothing renders into the panel during server rendering.

<Sidebar aria-label="Editor panels" isOpen={isOpen} hasPanel>
  <SidebarItem icon="folder" label="Directory" isSelected={isDirectory} onSelect={showDirectory}>
    <DirectoryTree /> {/* the panel, not the row */}
  </SidebarItem>
</Sidebar>

A SidebarItem given href is an anchor - Pressable renders one, and Enter follows it. It still sets aria-pressed from isSelected, which a link does not carry; a link marks the current page with aria-current. Its own attributes win over the item's, so pass them:

<SidebarItem
  icon="home"
  label="Home"
  href="/"
  isSelected
  aria-current="page"
  aria-pressed={undefined}
/>

onSelect still fires when the link is followed.

In a stacked rail

Under a Sidebar with layout="stacked", an item is its icon on a pill with the label under it. The selected fill and the hover tint are on the pill, the label takes the selected color and weight, and variant="plain" drops the fill. The label is a caption - size has no say in it.

Demos

For usage examples, visit the component demo page: Sidebar

Import

import { SidebarItem } from '@soroush.tech/design-system/Sidebar'

Props

Also accepts every Pressable prop except feedback and activeOpacity, which the variant decides (plain gets none, everything else gets the press highlight).

PropTypeDefaultDescription
iconIconName-Icon shown in both states, pinned to the anchored edge.
labelstring-Accessible name; also the default visible content while open.
variant'text' | 'outlined' | 'plain''text'text is borderless (selection via fill); plain also drops fill, hover, and press feedback.
size'sm' | 'md' | 'lg''sm'Padding and font size - resolves against theme.sizes.
colorPaletteColor'primary'Palette the selected fill and press highlight derive from.
childrenReactNode-Open-state content instead of the label - or the panel's content under hasPanel.
isSelectedboolean-Active state - drives aria-pressed and the selected fill.
onSelect() => void-Fired when the item is activated. Runs after any onClick.

Theming

SlotElement
rootThe item's button, or anchor.
pillThe pill behind the icon of an item in a stacked rail.

Customize via theme.components.SidebarItem.styleOverrides.

Source code

If this page does not answer your question, have a look at the implementation of the component for more detail.