Sidebar

App-shell sidebar primitives for full-page navigation layouts.

Overview

Sidebar provides the frame for an application shell: persistent navigation, a collapsible desktop rail, a mobile sheet, and an inset content region.

Open app shell example

Layout

import {
  Card,
  SidebarInset,
  SidebarProvider,
} from "@tilt-legal/cubitt-components/primitives";

export default function Page() {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        <div className="flex flex-1 flex-col gap-4 p-4">
          <div className="grid auto-rows-min gap-4 md:grid-cols-3">
            <Card className="aspect-video" />
            <Card className="aspect-video" />
            <Card className="aspect-video" />
          </div>
          <Card className="min-h-[100vh] flex-1 md:min-h-min" />
        </div>
      </SidebarInset>
    </SidebarProvider>
  );
}

App Sidebar

import { Sidebar } from "@tilt-legal/cubitt-components/primitives";

export function AppSidebar(props: React.ComponentProps<typeof Sidebar>) {
  return (
    <Sidebar variant="inset" {...props}>
      <AppSidebarHeader matters={data.matters} />
      <AppSidebarMain groups={data.navGroups} />
      <AppSidebarFooter
        isOrgAdmin={data.isOrgAdmin}
        organisationName={data.organisationName}
        user={data.user}
      />
    </Sidebar>
  );
}

Collapsible Groups

Use render to pass sidebar buttons into other primitives. This is Cubitt's replacement for shadcn-style asChild.

import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
  SidebarGroup,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSub,
  SidebarMenuSubButton,
  SidebarMenuSubItem,
} from "@tilt-legal/cubitt-components/primitives";
import { ChevronRight } from "@tilt-legal/cubitt-icons/ui/outline";
import { useState } from "react";

export function SidebarNavGroup({ items }) {
  return (
    <SidebarGroup>
      <SidebarGroupLabel>Platform</SidebarGroupLabel>
      <SidebarMenu>
        {items.map((item) => (
          <SidebarNavItem item={item} key={item.title} />
        ))}
      </SidebarMenu>
    </SidebarGroup>
  );
}

function SidebarNavItem({ item }) {
  const [isOpen, setIsOpen] = useState(Boolean(item.isActive));

  if (!item.items?.length) {
    return (
      <SidebarMenuItem>
        <SidebarMenuButton
          render={<a href={item.url} />}
          tooltip={item.title}
        >
          <item.icon />
          <span>{item.title}</span>
        </SidebarMenuButton>
      </SidebarMenuItem>
    );
  }

  return (
    <Collapsible
      onOpenChange={(open) => setIsOpen(open)}
      open={isOpen}
      render={<SidebarMenuItem />}
    >
      <CollapsibleTrigger
        render={<SidebarMenuButton tooltip={item.title} />}
      >
        <item.icon />
        <span>{item.title}</span>
        <ChevronRight
          className={`ml-auto transition-transform ${isOpen ? "rotate-90" : ""}`}
        />
      </CollapsibleTrigger>
      <CollapsiblePanel>
        <SidebarMenuSub>
          {item.items.map((subItem) => (
            <SidebarMenuSubItem key={subItem.title}>
              <SidebarMenuSubButton render={<a href={subItem.url} />}>
                <span>{subItem.title}</span>
              </SidebarMenuSubButton>
            </SidebarMenuSubItem>
          ))}
        </SidebarMenuSub>
      </CollapsiblePanel>
    </Collapsible>
  );
}
import {
  Menu,
  MenuContent,
  MenuItem,
  MenuSeparator,
  MenuTrigger,
  SidebarMenuAction,
} from "@tilt-legal/cubitt-components/primitives";
import { Dots, Folder, ShareRight, Trash2 } from "@tilt-legal/cubitt-icons/ui/outline";

<Menu>
  <MenuTrigger render={<SidebarMenuAction showOnHover />}>
    <Dots />
    <span className="sr-only">More</span>
  </MenuTrigger>
  <MenuContent side="right" align="start" className="w-48">
    <MenuItem>
      <Folder />
      <span>View Project</span>
    </MenuItem>
    <MenuItem>
      <ShareRight />
      <span>Share Project</span>
    </MenuItem>
    <MenuSeparator />
    <MenuItem variant="destructive">
      <Trash2 />
      <span>Delete Project</span>
    </MenuItem>
  </MenuContent>
</Menu>

On this page