Tessina UI
ConfiguratorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleCheckboxChipCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFile Upload 2FlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Sidebar

A composable, accessible sidebar shell for app layouts. Desktop and mobile modes, three variants (inset, floating, classic), collapsible to icon rail or off-canvas, keyboard shortcut toggle, LTR and RTL. Built on Base UI Collapsible with a full compound-component API.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import {
  SidebarProvider,
  Sidebar,
  SidebarHeader,
  SidebarContent,
  SidebarFooter,
  SidebarBrand,
  SidebarSearch,
  SidebarGroup,
  SidebarGroupLabel,
  SidebarGroupContent,
  SidebarMenu,
  SidebarMenuItem,
  SidebarMenuButton,
  SidebarMenuBadge,
  SidebarMenuAction,
  SidebarMenuSub,
  SidebarMenuSubItem,
  SidebarMenuSubButton,
  SidebarTrigger,
  SidebarInset,
  SidebarSeparator,
  useSidebar,
} from "@tessinaui/ui";
<SidebarProvider>
  <Sidebar>
    <SidebarHeader>
      <SidebarBrand name="Workspace" initial="W" />
      <SidebarSearch placeholder="Search…" />
    </SidebarHeader>

    <SidebarContent>
      <SidebarGroup>
        <SidebarGroupLabel>Navigation</SidebarGroupLabel>
        <SidebarGroupContent>
          <SidebarMenu>
            <SidebarMenuItem>
              <SidebarMenuButton active>
                <Home className="size-4" />
                <span>Home</span>
              </SidebarMenuButton>
            </SidebarMenuItem>
            <SidebarMenuItem>
              <SidebarMenuButton>
                <Inbox className="size-4" />
                <span>Inbox</span>
                <SidebarMenuBadge>12</SidebarMenuBadge>
              </SidebarMenuButton>
              <SidebarMenuAction aria-label="More">
                <MoreHorizontal className="size-3.5" />
              </SidebarMenuAction>
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarGroupContent>
      </SidebarGroup>
    </SidebarContent>

    <SidebarFooter>
      {/* footer actions */}
    </SidebarFooter>
  </Sidebar>

  <SidebarInset>
    {/* page content */}
  </SidebarInset>
</SidebarProvider>

The provider sets CSS custom properties for sidebar width and tracks open/collapsed state across the tree. Wrap any layout in SidebarProvider; sibling Sidebar and SidebarInset build a two-column app shell.

Examples

Default

A basic dashboard shell with a brand header, nav menu, footer, and collapsible icon rail.

Variants

Switch between the sidebar, floating, and inset shell treatments.

Inverse

A dark pro-tool rail using intent="inverse" with compact rows and project dots.

Collapsed rail

With collapsible="icon" and defaultOpen={false}, the sidebar starts as a thin icon rail.

Submenu

Nest links under a parent item with SidebarMenuSub, SidebarMenuSubItem, and SidebarMenuSubButton.

Skeleton

A loading placeholder via SidebarSkeleton — no SidebarProvider required.

When to use

  • Dashboard shells: primary navigation on the side of a content area — projects, inboxes, settings apps
  • Tool palettes: persistent lists of destinations, grouped and searchable
  • Editorial apps: folders, collections, pinned items with counts and submenus

If you just need a temporary overlay panel, reach for Drawer. If you need a top bar for a small-screen app, use TopHeaderMobile with a Drawer.

API Reference

SidebarProvider

Root state container. Provides sidebar context (open, openMobile, isMobile, size, dir) to the entire subtree. Also sets CSS variables (--sidebar-width, --sidebar-width-icon, --sidebar-width-mobile) on its wrapper.

PropTypeDefaultDescription
defaultOpenbooleantrueInitial open state on desktop
openboolean—Controlled open state
onOpenChange(open: boolean) => void—Called when open state changes
size"sm" | "md" | "lg""md"Controls width of expanded rail
dir"ltr" | "rtl""ltr"Text and layout direction
styleCSSProperties—Merged with the provider's own CSS variables

Also accepts any <div> attribute (className, id, etc.).

Keyboard shortcut: Cmd/Ctrl + B toggles the sidebar — on desktop it flips open, on mobile it flips openMobile.

Sidebar

The rail itself. Renders as <aside> on desktop and as a slide-in panel on mobile (via an internal backdrop + translate-x animation — no Dialog focus trap, so the sidebar stays in the page render tree).

PropTypeDefaultDescription
variant"sidebar" | "floating" | "inset""sidebar"Visual treatment. floating detaches the panel with margin + rounded border; inset pushes content content inward inside a shared surface
collapsible"offcanvas" | "icon" | "none""offcanvas"What "closed" means. icon collapses to a rail showing only icons; offcanvas hides the sidebar; none pins it open
side"left" | "right""left"Which edge the sidebar sits on. Automatically flipped under dir="rtl"
intent"default" | "inverse""default"default uses bg-background / text-foreground; inverse uses bg-foreground / text-background for a Noir-style dark rail

State data attributes (for CSS targeting):

  • data-state="open" \| "collapsed"
  • data-collapsible="icon" \| "offcanvas" \| "none"
  • data-variant="sidebar" \| "floating" \| "inset"
  • data-side="left" \| "right"
  • data-intent="default" \| "inverse"
  • data-mobile (present when rendering the mobile slide-in)

SidebarTrigger

Button that toggles the sidebar. Uses the appropriate handler based on isMobile. Accepts any <button> attribute and an onClick that runs alongside the toggle.

SidebarRail

Thin hit-area on the inner edge of the sidebar. Clicking it toggles open/collapsed. Useful when collapsible="icon" — gives the user an always-visible toggle target.

SidebarInset

Wrapper for page content that sits next to the sidebar. On desktop, reserves space using flexbox. On variant="inset", applies the shared surface border/radius.

SidebarHeader / SidebarFooter

Layout slots pinned to the top and bottom of the rail. Padded by default. Accept any <div> attribute.

SidebarBrand

Branded identity row. Shows an avatar tile with initial and a name/meta pair. Collapses gracefully to just the tile in icon mode.

PropTypeDefaultDescription
nameReactNode—Primary label (workspace name)
metaReactNode—Secondary label (plan, handle)
initialstring"A"One-letter avatar tile text
avatarReactNode—Replace the initial tile with a custom node
actionReactNode—Trailing node — commonly a chevron

SidebarSearch

Pre-styled search field. Icon on the start, optional shortcut hint on the end. Hidden when the rail is collapsed to icons. Accepts all <input> attributes plus:

PropTypeDefaultDescription
shortcutstring—Right-side shortcut hint (e.g. ⌘K)

SidebarContent

Main scroll region between header and footer. Accepts any <div> attribute.

SidebarSeparator

Thin horizontal divider. Padded on the sides to align with menu items.

SidebarGroup

Logical section. Can be collapsible.

PropTypeDefaultDescription
collapsiblebooleanfalseWrap the group in Base UI Collapsible so the label acts as a trigger
defaultOpenbooleantrueInitial open state of the collapsible group

SidebarGroupLabel

Section caption. If the group is collapsible, renders as a Collapsible.Trigger <button> with a chevron. Hidden when the rail is collapsed to icons.

SidebarGroupAction

Small icon button at the end of the label row (e.g. a + action). Hidden when the rail is collapsed to icons.

SidebarGroupContent

Wrapper for group children. When the parent SidebarGroup is collapsible, wraps in Collapsible.Panel.

SidebarMenu / SidebarMenuItem

List and list-item primitives. SidebarMenu renders <ul>, SidebarMenuItem renders <li> with position: relative so badges and actions can absolutely position.

SidebarMenuButton

The clickable row. Default renders as <button>; pass render={<a ...>…} to swap in an anchor or a Next.js <Link>.

PropTypeDefaultDescription
variant"default" | "ghost" | "pill""default"Hover/active treatment
size"sm" | "md" | "lg""md"Vertical density
activebooleanfalseApplies the active/selected styling
disabledbooleanfalseDims and blocks interaction
tooltipstring—Title attribute shown when collapsed to icons
renderReactElement—Swap the underlying element

Content: place an icon (<SomeIcon className="size-4" />) and a <span> label inside. The label is auto-hidden when the rail is collapsed to icons; the icon stays visible.

SidebarMenuAction

Small icon action that floats on the end of a menu row (e.g. three-dot "more" button). Renders as a <button>, so it must be a sibling of SidebarMenuButton inside SidebarMenuItem — never a child of the button (nested <button> elements are invalid HTML). Absolutely positioned against the menu item. Hidden when the rail is collapsed to icons.

<SidebarMenuItem>
  <SidebarMenuButton>…</SidebarMenuButton>
  <SidebarMenuAction aria-label="More">
    <MoreHorizontal className="size-3.5" />
  </SidebarMenuAction>
</SidebarMenuItem>

SidebarMenuBadge

Counter or status pill floated on the end of a menu row. Renders as a <span> and uses margin-inline-start: auto to push to the end of its flex container — so it belongs inside SidebarMenuButton (which is the flex row). Accepts any <span> attribute plus:

PropTypeDefaultDescription
tone"neutral" | "primary" | "success" | "warning" | "error" | "info""neutral"Maps to Tessina intent tokens (bg-*-light / text-*)
<SidebarMenuItem>
  <SidebarMenuButton>
    <Inbox className="size-4" />
    <span>Inbox</span>
    <SidebarMenuBadge>12</SidebarMenuBadge>
  </SidebarMenuButton>
</SidebarMenuItem>

Hidden when the rail is collapsed to icons.

SidebarMenuSub / SidebarMenuSubItem / SidebarMenuSubButton

Nested tree of links under a parent SidebarMenuItem. SidebarMenuSub renders <ul> with a start border to hint the tree; SidebarMenuSubButton behaves like SidebarMenuButton but slightly denser and without icon slot rules.

useSidebar()

React hook for consuming the sidebar context.

const { open, setOpen, openMobile, setOpenMobile, toggle, isMobile, size, dir } = useSidebar();

Throws if called outside a SidebarProvider.

Variants

VariantWhen to use
sidebarClassic app shell — flush against the edge, shares a border with content
floatingDetached card look — sits on an off-white surface with a soft border and rounded corners
insetModern dashboard — sidebar and content share a unified surface with inner padding

Collapsing behaviour

collapsibleClosed state
offcanvasSlides fully out of view; toggle brings it back
iconShrinks to an icon rail (--sidebar-width-icon); labels hide, icons stay
noneAlways open — no toggle behaviour

RTL

Pass dir="rtl" on SidebarProvider. The sidebar flips sides automatically, chevrons and badge positions mirror, and logical CSS (border-s, ms-auto, ps-) keeps paddings consistent. All compound parts respect the direction via context.

Accessibility

  • Navigation is a semantic <nav> with aria-label="Sidebar".
  • Collapsible groups use Base UI Collapsible — the label is a real <button> with aria-expanded.
  • The rail target is a <button> with aria-label="Toggle Sidebar".
  • Mobile overlay is dismissible via backdrop click or the Escape key; focus returns to the trigger on close.
  • All menu buttons respond to Space / Enter and receive a visible focus ring (focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2).
  • Cmd/Ctrl + B toggles the sidebar globally.

Shortcut

Keyboard shortcut badges for documenting and displaying key combinations. Includes a standalone Key component for individual keys and a Shortcut wrapper for combos with configurable separators and RTL support.

Skeleton

Animated placeholder for loading states. Wave shimmer (default) and pulse variants, five rounded options, and composable with any layout.

On this page

PlaygroundInstallationUsageExamplesDefaultVariantsInverseCollapsed railSubmenuSkeletonWhen to useAPI ReferenceSidebarProviderSidebarSidebarTriggerSidebarRailSidebarInsetSidebarHeader / SidebarFooterSidebarBrandSidebarSearchSidebarContentSidebarSeparatorSidebarGroupSidebarGroupLabelSidebarGroupActionSidebarGroupContentSidebarMenu / SidebarMenuItemSidebarMenuButtonSidebarMenuActionSidebarMenuBadgeSidebarMenuSub / SidebarMenuSubItem / SidebarMenuSubButtonuseSidebar()VariantsCollapsing behaviourRTLAccessibility