Components

A side-sheet drawer — built on the same Base UI Dialog primitive as Dialog, but the popup is a full-height panel that slides in from the edge. Drawer is the state root; trigger and close render your element via Base UI's render prop, so a Button keeps its full API.

Install

npx shadcn@latest add @alix/drawer

Preview

Usage

<Drawer>
  <DrawerTrigger render={<Button>Filters</Button>} />
  <DrawerContent side="right">
    <DrawerHeader>
      <DrawerTitle>Filters</DrawerTitle>
      <DrawerDescription>Narrow the results.</DrawerDescription>
    </DrawerHeader>
    <DrawerBody>form fields</DrawerBody>
    <DrawerFooter>
      <DrawerClose render={<Button variant="outline">Cancel</Button>} />
      <Button onClick={() => apply()}>Apply</Button>
    </DrawerFooter>
  </DrawerContent>
</Drawer>

Examples

01

Left-side navigation

Use side="left" when the drawer behaves like a temporary navigation or workspace switcher.

<Drawer>
  <DrawerTrigger render={<Button variant="outline">Open workspace</Button>} />
  <DrawerContent side="left">
    <DrawerHeader>
      <DrawerTitle>Workspaces</DrawerTitle>
      <DrawerDescription>Switch between active engagements.</DrawerDescription>
    </DrawerHeader>
    <DrawerBody className="flex flex-col gap-2">
      <a className="border border-border p-3 text-sm" href="#">Project Atlas</a>
      <a className="border border-border p-3 text-sm" href="#">Northstar Retail</a>
      <a className="border border-border p-3 text-sm" href="#">Cobalt Health</a>
    </DrawerBody>
  </DrawerContent>
</Drawer>
02

Filter panel

Drawer gives filters room for fields and checkbox groups while keeping apply actions pinned in the footer.

<Drawer>
  <DrawerTrigger render={<Button>Filter engagements</Button>} />
  <DrawerContent side="right">
    <DrawerHeader>
      <DrawerTitle>Filter engagements</DrawerTitle>
      <DrawerDescription>Refine the active portfolio view.</DrawerDescription>
    </DrawerHeader>
    <DrawerBody className="flex flex-col gap-5">
      <Field label="Capability">
        <Select
          placeholder="Any capability"
          items={[
            { value: "turnaround", label: "Turnaround" },
            { value: "transactions", label: "Transactions" },
          ]}
        />
      </Field>
      <div className="flex flex-col gap-3">
        <span className="text-sm font-medium text-foreground">Status</span>
        <label className="flex items-center gap-2 text-sm">
          <Checkbox defaultChecked /> On track
        </label>
        <label className="flex items-center gap-2 text-sm">
          <Checkbox /> At risk
        </label>
      </div>
    </DrawerBody>
    <DrawerFooter>
      <DrawerClose render={<Button variant="outline">Reset</Button>} />
      <DrawerClose render={<Button>Apply filters</Button>} />
    </DrawerFooter>
  </DrawerContent>
</Drawer>
03

Engagement detail

A detail drawer can scan like a side panel with badges, facts, and secondary actions.

<Drawer>
  <DrawerTrigger render={<Button variant="outline">View details</Button>} />
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Project Atlas</DrawerTitle>
      <DrawerDescription>Turnaround and restructuring</DrawerDescription>
    </DrawerHeader>
    <DrawerBody className="flex flex-col gap-4">
      <div className="flex flex-wrap gap-2">
        <Badge variant="success" dot>On track</Badge>
        <Badge variant="primary">Phase 2</Badge>
      </div>
      <dl className="grid grid-cols-2 gap-4 text-sm">
        <div>
          <dt className="text-muted-foreground">Lead</dt>
          <dd className="font-medium text-foreground">Amelia Ward</dd>
        </div>
        <div>
          <dt className="text-muted-foreground">Next review</dt>
          <dd className="font-medium text-foreground">Friday</dd>
        </div>
      </dl>
    </DrawerBody>
    <DrawerFooter>
      <DrawerClose render={<Button variant="outline">Close</Button>} />
      <Button>Open workspace</Button>
    </DrawerFooter>
  </DrawerContent>
</Drawer>

When to use it

Use it for

  • Filters, navigation, and detail panels that need more room than a popover while preserving page context.
  • Longer side content with a scrollable body and persistent footer actions.
  • Right-side task panels and left-side workspace or navigation panels.

Reach for something else

  • Focused blocking confirmation or create flows — use Dialog instead.
  • Small anchored explanations or local edits — use Popover instead.
  • Command lists with only actions or links — use Menu instead.
  • Non-blocking save confirmations — use Toast instead.

Props

PropTypeDefaultDescription
Draweropen?, defaultOpen?, onOpenChange?, modal?The state root (shared Base UI Dialog.Root).
DrawerContentside?: "right" | "left""right"The portalled scrim + full-height side panel; side picks the edge it anchors to and slides in from. Includes an automatic close button.
DrawerTrigger / DrawerCloserender: ReactElementOpen/close via the render prop, keeping the passed element's full API.
DrawerHeader / DrawerTitle / DrawerDescription / DrawerBody / DrawerFooterchildrenStructural slots: bordered header, scrollable body region, and a bordered, right-aligned footer action row.

See also