Drawer
A swipeable panel that slides in from an edge of the screen — dismiss it by swiping, dragging the handle, or tapping outside. Built on Base UI's drawer primitive.
Last updated January 1, 1980
Copied from shadcn/ui and coss ui.
- Adds a logical position="start"/"end" on top of the physical top/right/bottom/left, resolved from the ambient direction measured at the trigger, and used to derive swipeDirection automatically
- DrawerMenuTrigger's chevron flips with rtl:-scale-x-100, and menu item padding/checkbox thumb travel use logical pe-/ps- and a rtl:-translate-x flip instead of physical left/right values
- Guards against a spurious open-then-instant-dismiss: in re-render-heavy apps, the interaction that opens the drawer can produce a follow-up click Base UI reads as an outside-press in the same tick. Any outside-press within 200ms of opening is treated as that ghost press and cancelled
- Fixed two positioning bugs: justify-[right]/justify-[left] silently failed to generate any CSS (those keywords aren't part of Tailwind's justify-content value scale for the shorthand form), so every non-bottom/top drawer rendered on the physical left regardless of position — replaced with the [justify-content:right]/[justify-content:left] arbitrary-property form. Separately, DrawerPopup reserves space for the drag bar via padding on itself when showBar is set, which inset every child including DrawerFooter's own background — DrawerFooter now cancels that inset with a matching negative margin so its background reaches the handle edge
Overview
Installation
Usage
import {
Drawer,
DrawerClose,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
export function Example() {
return (
<Drawer>
<DrawerTrigger render={<Button variant="outline">Open</Button>} />
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>Edit profile</DrawerTitle>
<DrawerDescription>
Make changes to your profile here.
</DrawerDescription>
</DrawerHeader>
<DrawerPanel>{/* content */}</DrawerPanel>
<DrawerFooter>
<Button type="submit">Save changes</Button>
<DrawerClose render={<Button variant="outline">Cancel</Button>} />
</DrawerFooter>
</DrawerPopup>
</Drawer>
)
}Examples
No footer
DrawerFooter is optional — skip it for a purely informational sheet that only needs to be swiped away.
Positions
Pass position to control which edge the drawer slides in from.
Variants
default rounds the corners touching the screen edge, straight has no rounding and skips the nested-drawer scale-down effect, and inset floats with a margin from the edge on larger screens. force-inset applies that same floating-card treatment at every viewport size, including mobile.
Scrollable content
DrawerHeader and DrawerFooter stay fixed outside the scroll container — DrawerPanel is the only part that scrolls.
Snap points
Pass snapPoints to have the drawer settle at preset heights instead of following the drag freely. Numbers between 0 and 1 are fractions of the viewport height. Snap points only apply to position="bottom" drawers.
Nested drawers
A drawer opened from inside another already-open drawer stacks on top of it, scaling the one behind it down slightly. Pass modal="trap-focus" on the inner drawer — the outer one already owns the page-level scroll lock, so the inner one should only trap focus.
RTL
position="start" opens from the reading-start edge — the right in RTL, the left in LTR — instead of a hardcoded side.