Components / Layout

Sidebar

The console's sidebar: logo, nav sections with icons, a meta slot and status tags per row, nested sub-links, and a drawer below lg. Compose it with Topbar in an AppShell; the Explorer's sidebar specializes it.

Extracted fromExplorer

Import

tsx
import { Sidebar, AppShell, Topbar } from "@somnia-chain/ui"
// in a Next.js app, internal links use next/link:
import { Sidebar, AppShell, Topbar } from "@somnia-chain/ui/next"

Examples

Rows with meta

meta sits after the label and before the status tag: a count, or a dot for work in flight. Pressing a row makes it current. Below lg the sidebar is a closed drawer, so here its rows only respond on a wider screen.

examples/sidebar/meta.tsxShow code
tsx
"use client"

import { LiveDot, type NavSection, Sidebar } from "@somnia-chain/ui"
import { Bell, Bot, FlaskConical, FolderGit2, ShieldCheck } from "lucide-react"

import { useInPlaceTabs } from "@/components/use-in-place-tabs"

const SECTIONS: NavSection[] = [
  {
    label: "Attention",
    items: [{ href: "#alerts", label: "Alerts", icon: Bell, meta: <span className="text-down">3</span> }],
  },
  {
    label: "Agents",
    items: [
      { href: "#engineer", label: "Engineer", icon: Bot, meta: <LiveDot tone="warn" aria-label="Running" /> },
      {
        href: "#security",
        label: "Security",
        icon: ShieldCheck,
        meta: <LiveDot tone="accent" pulse={false} aria-label="Claimed" />,
      },
      { href: "#qa", label: "QA bot", icon: FlaskConical, status: "soon" },
    ],
  },
  { label: "Config", items: [{ href: "#repos", label: "Repos", icon: FolderGit2, meta: "12" }] },
]

export default function SidebarMeta() {
  // In an app, `currentPath` is the route and each href a page. Here pressing a row just makes it current.
  const { current, linkAs } = useInPlaceTabs("#engineer")
  return (
    // A Sidebar is fixed to the window's left edge below `lg` and as tall as the window above it; this box holds it in
    // place: paint containment makes it the box a fixed child is placed in, and the overrides keep the drawer on screen
    <div className="relative h-[400px] w-[264px] max-w-full overflow-hidden rounded-[10px] [contain:paint] [&_aside]:h-full [&_aside]:translate-x-0">
      <Sidebar
        currentPath={current}
        linkAs={linkAs}
        sections={SECTIONS}
        badge="Console"
        homeHref="#"
        navLabel="Example console"
      />
    </div>
  )
}

Props

Sidebar

PropTypeDefaultDescription
currentPathstring—The router's current pathname. Drives the active row.
sections{ label?, items: NavItem[] }[]—Navigation tree.
open / onCloseboolean / () => voidfalseDrawer state below lg. AppShell passes both.
badgestring—Label beside the logo.
beforeNavReactNode—Slot between the header and the nav.
footerLinks{ href, label }[]—External links pinned to the bottom.
navLabelstring"Main"Accessible name of the nav.
linkAsLinkComponent<a>Component for internal links. @somnia-chain/ui/next sets it to next/link.

NavItem

PropTypeDefaultDescription
href / labelstring—The row's link.
iconComponentType<{ className?, strokeWidth? }>—E.g. a lucide icon.
metaReactNode—After the label, before the status tag: a count of open alerts, or a LiveDot.
status"live" | "legacy" | "soon" | "todo"—Anything but live is a small tag after the label.
links{ href, label }[]—Sub-pages, nested under the row while it is active.
matchstring[]—Further path prefixes that make the row active.