Clidey UXDocs

Drawer

Drawer slides in from any screen edge (top, bottom, left, right). It's powered by the Vaul library and supports drag-to-dismiss on mobile.

Interactive example#

Drawer · Live example

12 tasks per week

Open the drawer, change the weekly goal, then close it by dragging or using Done.

The code uses shared demo presentation helpers. Their complete source and styling are included in the LLM build context.

Import#

TSX

import {
  Drawer,
  DrawerTrigger,
  DrawerContent,
  DrawerHeader,
  DrawerTitle,
  DrawerDescription,
  DrawerFooter,
  DrawerClose,
} from '@clidey/ux';

Basic drawer (bottom, default)#

TSX

<Drawer>
  <DrawerTrigger>
    <Button variant="outline">Open drawer</Button>
  </DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Move Goal</DrawerTitle>
      <DrawerDescription>
        Set your daily activity goal.
      </DrawerDescription>
    </DrawerHeader>
    <div className="p-4">
      {/* content */}
    </div>
    <DrawerFooter>
      <Button>Submit</Button>
      <DrawerClose>
        <Button variant="outline">Cancel</Button>
      </DrawerClose>
    </DrawerFooter>
  </DrawerContent>
</Drawer>

Directional variants#

Change the direction prop on the Drawer component to slide in from a different edge:

TSX

{/* From the right — good for detail panels */}
<Drawer direction="right">
  <DrawerTrigger>
    <Button>Open right panel</Button>
  </DrawerTrigger>
  <DrawerContent className="h-full w-80">
    <DrawerHeader>
      <DrawerTitle>Details</DrawerTitle>
    </DrawerHeader>
    <div className="p-4">{/* content */}</div>
  </DrawerContent>
</Drawer>

{/* From the top */}
<Drawer direction="top">
  <DrawerTrigger>
    <Button>Open top panel</Button>
  </DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Notification center</DrawerTitle>
    </DrawerHeader>
  </DrawerContent>
</Drawer>

Mobile bottom sheet#

The default bottom-positioned drawer works great as a mobile action sheet:

TSX

<Drawer>
  <DrawerTrigger>
    <Button size="icon" variant="ghost">
      <MoreVertical className="size-4" />
    </Button>
  </DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Actions</DrawerTitle>
    </DrawerHeader>
    <div className="p-4 space-y-2">
      <Button variant="ghost" className="w-full justify-start">
        <Edit className="size-4 mr-2" /> Edit
      </Button>
      <Button variant="ghost" className="w-full justify-start">
        <Share className="size-4 mr-2" /> Share
      </Button>
      <Button variant="ghost" className="w-full justify-start text-destructive">
        <Trash className="size-4 mr-2" /> Delete
      </Button>
    </div>
  </DrawerContent>
</Drawer>

Sub-components#

ComponentDescription
DrawerRoot. Accepts direction, open, onOpenChange.
DrawerTriggerOpens the drawer. Renders as its child automatically for ux components (Button, Badge, ...) and intrinsic elements, so no asChild is needed; pass asChild only for other ref-forwarding elements. See Triggers and asChild.
DrawerPortalRenders drawer outside the DOM tree. Used internally.
DrawerOverlayBackdrop layer using the --backdrop CSS token. Used internally.
DrawerContentThe sliding panel.
DrawerHeaderTop section with title/description.
DrawerTitleAccessible heading.
DrawerDescriptionSupporting text.
DrawerFooterBottom section for actions.
DrawerCloseCloses the drawer. Use asChild.

Drawer Props

PropTypeDefaultDescription
direction"top" | "bottom" | "left" | "right""bottom"Side the drawer slides in from.
openboolean—Controlled open state.
onOpenChange(open: boolean) => void—Called when open state changes.
shouldScaleBackgroundbooleanfalseScales down the page background when open.

Was this page helpful?