Agents (llms.txt)

Sidebar

Structural layout primitive for sidebar navigation. Provides Header, Content, Footer, and Group slots.

Workspace
Inbox
My Issues

Workspace

Initiatives
Projects
Views
chris@playstack.co
import { Sidebar } from '@/components/sidebar';

export const meta = {
  layout: 'padded',
};

export default function SidebarPreview() {
  return (
    <div className="h-96 w-64 overflow-hidden rounded-lg border border-border bg-background">
      <Sidebar>
        <Sidebar.Header>
          <div className="font-medium text-sm">Workspace</div>
        </Sidebar.Header>
        <Sidebar.Content>
          <Sidebar.Group>
            <div className="px-2 py-1 text-sm">Inbox</div>
            <div className="px-2 py-1 text-sm">My Issues</div>
          </Sidebar.Group>
          <Sidebar.Group label="Workspace">
            <div className="px-2 py-1 text-sm">Initiatives</div>
            <div className="px-2 py-1 text-sm">Projects</div>
            <div className="px-2 py-1 text-sm">Views</div>
          </Sidebar.Group>
        </Sidebar.Content>
        <Sidebar.Footer>
          <div className="text-foreground-secondary text-sm">
            chris@playstack.co
          </div>
        </Sidebar.Footer>
      </Sidebar>
    </div>
  );
}

Dependencies

Source Code

import { Slot } from '@/components/slot';
import { cn } from '@/lib/utils/classnames';

interface SidebarProps extends React.ComponentPropsWithRef<'div'> {
  asChild?: boolean;
}

const Sidebar = ({ ref, className, asChild, ...rest }: SidebarProps) => {
  const Comp = asChild ? Slot : 'div';
  return (
    <Comp
      ref={ref}
      data-sidebar
      className={cn('flex h-full flex-col', className)}
      {...rest}
    />
  );
};

const SidebarHeader = ({
  ref,
  className,
  ...rest
}: React.ComponentPropsWithRef<'div'>) => (
  <div
    ref={ref}
    data-sidebar-header
    className={cn('sticky top-0 shrink-0 p-3', className)}
    {...rest}
  />
);

const SidebarContent = ({
  ref,
  className,
  ...rest
}: React.ComponentPropsWithRef<'div'>) => (
  <div
    ref={ref}
    data-sidebar-content
    className={cn('flex-1 overflow-y-auto', className)}
    {...rest}
  />
);

const SidebarFooter = ({
  ref,
  className,
  ...rest
}: React.ComponentPropsWithRef<'div'>) => (
  <div
    ref={ref}
    data-sidebar-footer
    className={cn(
      'sticky bottom-0 shrink-0 border-border border-t p-3',
      className
    )}
    {...rest}
  />
);

interface SidebarGroupProps extends React.ComponentPropsWithRef<'div'> {
  label?: React.ReactNode;
}

const SidebarGroup = ({
  ref,
  className,
  label,
  children,
  ...rest
}: SidebarGroupProps) => (
  <div
    ref={ref}
    data-sidebar-group
    className={cn('flex flex-col gap-1 py-2', className)}
    {...rest}
  >
    {label ? (
      <p className="px-2 py-1 font-medium text-foreground-secondary/70 text-xs uppercase tracking-wider">
        {label}
      </p>
    ) : null}
    <div className="flex flex-col gap-0.5">{children}</div>
  </div>
);

const CompoundSidebar = Object.assign(Sidebar, {
  Header: SidebarHeader,
  Content: SidebarContent,
  Footer: SidebarFooter,
  Group: SidebarGroup,
});

export type { SidebarGroupProps, SidebarProps };
export { CompoundSidebar as Sidebar };

Server component. No 'use client' — sidebar chrome layout compound; forwards props only.

Sidebar is a structural layout primitive for building sidebar navigation regions. It is unstyled with respect to background — sit it inside AppShell.Sidebar (which provides the background and border) and use the Header / Content / Footer / Group slots to lay out workspace switchers, nav groups, and user rows.

Anatomy


          <Sidebar>
  <Sidebar.Header>{workspaceSwitcher}</Sidebar.Header>
  <Sidebar.Content>
    <Sidebar.Group>{primaryNav}</Sidebar.Group>
    <Sidebar.Group label="Workspace">{workspaceNav}</Sidebar.Group>
  </Sidebar.Content>
  <Sidebar.Footer>{userRow}</Sidebar.Footer>
</Sidebar>
        

API Reference

Extends the div element.

Prop Default Type Description
asChild false boolean Render as the provided child element. Use to swap the default `<div>` for a semantic `<aside>` landmark without forking the component.

asChild

Sidebar renders a <div> by default so it can sit inside AppShell.Sidebar (which already renders an <aside> landmark). When you use Sidebar standalone — outside AppShell — pass asChild to swap the root for an <aside> so the navigation region is exposed as a landmark to assistive tech.


          <Sidebar asChild>
  <aside aria-label="Primary">
    <Sidebar.Header>{workspaceSwitcher}</Sidebar.Header>
    <Sidebar.Content>{nav}</Sidebar.Content>
  </aside>
</Sidebar>
        

Previous

Settings Row

Next

Skeleton