agentic-ui logoagentic-ui

Drawer

A panel that slides in from any edge of the screen with swipe-to-dismiss gestures. Supports left, right, top, and bottom orientations.

All sides

import { Drawer } from "@brijbyte/agentic-ui/drawer";
import { Button } from "@brijbyte/agentic-ui/button";
import { Drawer as BaseDrawer } from "@base-ui/react/drawer";
import type { DrawerSide } from "@brijbyte/agentic-ui/drawer";

import "@brijbyte/agentic-ui/drawer.css";
import "@brijbyte/agentic-ui/button.css";

const SIDES: DrawerSide[] = ["right", "left", "bottom", "top"];

export default function SidesDemo() {
  return (
    <div style={{ display: "flex", gap: "var(--space-2)", flexWrap: "wrap" }}>
      {SIDES.map((side) => (
        <Drawer
          key={side}
          side={side}
          trigger={
            <Button variant="outline" size="sm">
              {side}
            </Button>
          }
          title={`${side.charAt(0).toUpperCase() + side.slice(1)} drawer`}
          description="Slides in from the edge. Swipe to dismiss."
          footer={
            <Button variant="soft" size="sm" render={<BaseDrawer.Close />}>
              Done
            </Button>
          }
        >
          <p style={{ fontFamily: "var(--font-mono)", fontSize: "var(--font-size-sm)", color: "var(--color-secondary)", margin: 0 }}>
            Content goes here. This drawer slides in from the <strong>{side}</strong> edge and supports swipe-to-dismiss.
          </p>
        </Drawer>
      ))}
    </div>
  );
}

Bottom sheet

import { Drawer } from "@brijbyte/agentic-ui/drawer";
import { Drawer as BaseDrawer } from "@base-ui/react/drawer";
import { Button } from "@brijbyte/agentic-ui/button";
import { Separator } from "@brijbyte/agentic-ui/separator";

import "@brijbyte/agentic-ui/drawer.css";
import "@brijbyte/agentic-ui/button.css";
import "@brijbyte/agentic-ui/separator.css";

const ACTIONS = ["Rename", "Duplicate", "Move to folder", "Download"];

export default function BottomSheetDemo() {
  return (
    <Drawer
      side="bottom"
      trigger={
        <Button variant="outline" size="sm">
          File options
        </Button>
      }
      title="report-q4.pdf"
      dismissible={false}
      footer={
        <Button variant="solid" tone="destructive" size="sm" render={<BaseDrawer.Close />}>
          Delete file
        </Button>
      }
    >
      <div style={{ display: "flex", flexDirection: "column" }}>
        {ACTIONS.map((action, i) => (
          <div key={action}>
            {i > 0 && <Separator />}
            <button
              type="button"
              style={{
                display: "block",
                width: "100%",
                padding: "var(--space-3) 0",
                background: "none",
                border: "none",
                textAlign: "left",
                fontFamily: "var(--font-mono)",
                fontSize: "var(--font-size-sm)",
                color: "var(--color-primary)",
                cursor: "pointer",
              }}
            >
              {action}
            </button>
          </div>
        ))}
      </div>
    </Drawer>
  );
}

Composed: base-ui Root + styled parts

import { Drawer, Drawer as BaseDrawer } from "@base-ui/react/drawer";
import {
  DrawerBackdrop,
  DrawerViewport,
  DrawerPopup,
  DrawerContent,
  DrawerTitle,
  DrawerDescription,
  DrawerClose,
  DrawerFooter,
} from "@brijbyte/agentic-ui/drawer";
import { Button } from "@brijbyte/agentic-ui/button";

import "@brijbyte/agentic-ui/drawer.css";
import "@brijbyte/agentic-ui/button.css";

export default function ComposedDemo() {
  return (
    <Drawer.Root swipeDirection="right">
      <Drawer.Trigger
        render={
          <Button variant="ghost" size="sm">
            Open (composed)
          </Button>
        }
      />
      <Drawer.Portal>
        <DrawerBackdrop />
        <DrawerViewport side="right">
          <DrawerPopup side="right" style={{ position: "relative" }}>
            <DrawerContent>
              <DrawerTitle>Settings</DrawerTitle>
              <DrawerDescription>Composed with raw base-ui Root and styled parts.</DrawerDescription>
              <p
                style={{
                  fontFamily: "var(--font-mono)",
                  fontSize: "var(--font-size-sm)",
                  color: "var(--color-secondary)",
                  margin: 0,
                }}
              >
                You have full control over the base-ui Root behaviour while keeping all design-system styling via the styled parts.
              </p>
              <DrawerFooter>
                <Button variant="outline" size="sm" render={<BaseDrawer.Close />}>
                  Close
                </Button>
                <Button variant="solid" size="sm">
                  Save
                </Button>
              </DrawerFooter>
            </DrawerContent>
            <DrawerClose aria-label="Close drawer" />
          </DrawerPopup>
        </DrawerViewport>
      </Drawer.Portal>
    </Drawer.Root>
  );
}

API Reference

A panel that slides in from any edge of the screen with swipe-to-dismiss
gestures. Supports left, right, top, and bottom orientations with an
optional drag handle bar.

PropTypeDefault
triggerReactElement<unknown, string | JSXElementConstructor<any>>

Element that triggers the drawer — rendered as a Drawer.Trigger.

side"left" | "right" | "bottom" | "top"right

Which edge the drawer slides in from.

titleReactNode

Heading rendered at the top of the drawer.

descriptionReactNode

Supplementary text below the title.

footerReactNode

Content rendered at the bottom of the drawer.

handleBarbooleantrue for bottom/top

Show a drag handle bar (useful for bottom/top drawers).

dismissiblebooleantrue

Show a close button in the top-right corner.

openboolean

Controlled open state.

defaultOpenboolean

Whether the drawer is initially open (uncontrolled).

onOpenChange((open: boolean) => void)

Called when the drawer opens or closes.

Styled Parts

base-ui docs ↗

Pre-styled wrappers around the corresponding base-ui parts. All base-ui props are forwarded.

CSS Class Names

Available as keys on the DrawerStyles object. Each key maps to a hashed CSS module class name at runtime.

backdropclosecontentdescriptionfooterhandle-barpopuppopup-bottompopup-leftpopup-rightpopup-toptitleviewport