GitHub

Drawer

A panel that slides in from any edge of the screen, with swipe-to-dismiss support.

CMSPayload CMS Integration

Drawers work well for displaying CMS content like navigation menus, filters, or detailed information panels.

CMS Demo

See how content editors configure drawers in a CMS:

Loading...
CMS ConfigurationSimulates Payload CMS drawer configuration

Basic Usage

Loading...
DrawerSliding panel with swipe support

Installation

pnpm add @corew500/ui

Usage

import {
  Drawer,
  DrawerTrigger,
  DrawerContent,
  DrawerHeader,
  DrawerTitle,
  DrawerDescription,
  DrawerFooter,
  DrawerClose,
} from "@mordecai-design-system/ui"
<Drawer>
  <DrawerTrigger>Open Drawer</DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Drawer Title</DrawerTitle>
      <DrawerDescription>Description here</DrawerDescription>
    </DrawerHeader>
    <p>Content</p>
    <DrawerFooter>
      <Button>Submit</Button>
      <DrawerClose>Cancel</DrawerClose>
    </DrawerFooter>
  </DrawerContent>
</Drawer>

Examples

Directions

// Bottom (default)
<Drawer>
  <DrawerContent>Slides from bottom</DrawerContent>
</Drawer>

// Right
<Drawer direction="right">
  <DrawerContent>Slides from right</DrawerContent>
</Drawer>

// Left
<Drawer direction="left">
  <DrawerContent>Slides from left</DrawerContent>
</Drawer>

// Top
<Drawer direction="top">
  <DrawerContent>Slides from top</DrawerContent>
</Drawer>

Controlled

const [open, setOpen] = useState(false)

<Drawer open={open} onOpenChange={setOpen}>
  <DrawerTrigger>Open</DrawerTrigger>
  <DrawerContent>
    <Button onClick={() => setOpen(false)}>Close</Button>
  </DrawerContent>
</Drawer>

With Form

<DrawerContent>
  <DrawerHeader>
    <DrawerTitle>Edit Settings</DrawerTitle>
  </DrawerHeader>
  <form className="p-4 space-y-4">
    <Input placeholder="Name" />
    <Input type="email" placeholder="Email" />
  </form>
  <DrawerFooter>
    <Button>Save</Button>
    <DrawerClose>Cancel</DrawerClose>
  </DrawerFooter>
</DrawerContent>

Sub-components

| Component | Description | |-----------|-------------| | Drawer | Root component with direction control | | DrawerTrigger | Button that opens the drawer | | DrawerContent | The sliding panel | | DrawerHeader | Header area | | DrawerTitle | Main heading | | DrawerDescription | Supporting text | | DrawerFooter | Footer with actions | | DrawerClose | Button to close | | DrawerOverlay | Background overlay |

Props

Drawer

PropTypeDefaultDescription
activeSnapPointenum
setActiveSnapPoint(snapPoint: string | number) => void
openenum
closeThresholdnumber0.25Number between 0 and 1 that determines when the drawer should be closed. Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more.
noBodyStylesenumWhen `true` the `body` doesn't get any styles assigned from Vaul
onOpenChange(open: boolean) => void
shouldScaleBackgroundenum
setBackgroundColorOnScaleenumtrueWhen `false` we don't change body's background color when the drawer is open.
scrollLockTimeoutnumber500msDuration for which the drawer is not draggable after scrolling content inside of the drawer.
fixedenumWhen `true`, don't move the drawer upwards if there's space, but rather only change it's height so it's fully scrollable when the keyboard is open
handleOnlyenumfalseWhen `true` only allows the drawer to be dragged by the `<Drawer.Handle />` component.
dismissibleenumtrueWhen `false` dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in comination with the `open` prop, otherwise you won't be able to open/close the drawer.
onDrag(event: PointerEvent<HTMLDivElement>, percentageDragged: number) => void
onRelease(event: PointerEvent<HTMLDivElement>, open: boolean) => void
modalenumtrueWhen `false` it allows to interact with elements outside of the drawer without closing it.
nestedenum
onClose() => void
directionenum'bottom'Direction of the drawer. Can be `top` or `bottom`, `left`, `right`.
defaultOpenenumfalseOpened by default, skips initial enter animation. Still reacts to `open` state changes
disablePreventScrollenumfalseWhen set to `true` prevents scrolling on the document body on mount, and restores it on unmount.
repositionInputsenumtrue when {@link snapPoints } is definedWhen `true` Vaul will reposition inputs rather than scroll then into view if the keyboard is in the way. Setting it to `false` will fall back to the default browser behavior.
snapToSequentialPointenumfalseDisabled velocity based swiping for snap points. This means that a snap point won't be skipped even if the velocity is high enough. Useful if each snap point in a drawer is equally important.
containerHTMLElement
onAnimationEnd(open: boolean) => voidGets triggered after the open or close animation ends, it receives an `open` argument with the `open` state of the drawer by the time the function was triggered. Useful to revert any state changes for example.
preventScrollRestorationenum
autoFocusenum
snapPoints(string | number)[]Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Example `[0.2, 0.5, 0.8]`. You can also use px values, which doesn't take screen height into account.
fadeFromIndexnumberIndex of a `snapPoint` from which the overlay fade should be applied. Defaults to the last snap point.

DrawerContent

Inherits Vaul drawer content props.

Features

  • Swipe to dismiss: Natural gesture support on touch devices
  • Directional: Slides from any edge
  • Accessible: Full keyboard and screen reader support
  • Responsive: Great for mobile navigation patterns