Edit profile
Make changes to your profile and save when you are done.
---
import { Button } from "@/components/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
---
<Drawer>
<DrawerTrigger variant="outline">Open drawer</DrawerTrigger>
<DrawerContent showSwipeHandle class="h-[60dvh]">
<DrawerHeader>
<DrawerTitle>Edit profile</DrawerTitle>
<DrawerDescription>
Make changes to your profile and save when you are done.
</DrawerDescription>
</DrawerHeader>
<div class="grid gap-4 p-4">
<div class="bg-muted h-24 rounded-lg"></div>
<div class="bg-muted h-24 rounded-lg"></div>
</div>
<DrawerFooter>
<Button>Save changes</Button>
<DrawerClose variant="outline">Cancel</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>Installation
npx shadcn@latest add @fulldev/drawer
Usage
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
<Drawer swipeDirection="down">
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent showSwipeHandle>
<DrawerHeader>
<DrawerTitle>Drawer title</DrawerTitle>
<DrawerDescription>Drawer description</DrawerDescription>
</DrawerHeader>
<div class="p-4">Drawer content</div>
<DrawerFooter>
<DrawerClose variant="outline">Close</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
Composition
DrawerContent renders the required portal, backdrop, viewport, popup, and
inner content structure. Use the lower-level exports when you need to customize
that structure. Its inner content wrapper scrolls vertically when the panel
contains more content than the available viewport.
Drawer
├── DrawerTrigger
└── DrawerContent
├── DrawerSwipeHandle
├── DrawerHeader
│ ├── DrawerTitle
│ └── DrawerDescription
├── content
└── DrawerFooter
└── DrawerClose
Examples
Directions
The swipe direction also chooses the edge where the drawer opens.
down drawer
Swipe down or use the close button.
up drawer
Swipe up or use the close button.
left drawer
Swipe left or use the close button.
right drawer
Swipe right or use the close button.
---
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
const directions = ["down", "up", "left", "right"] as const
---
<div class="flex flex-wrap gap-2">
{
directions.map((direction) => (
<Drawer swipeDirection={direction}>
<DrawerTrigger variant="outline" class="capitalize">
{direction}
</DrawerTrigger>
<DrawerContent showSwipeHandle>
<DrawerHeader>
<DrawerTitle class="capitalize">{direction} drawer</DrawerTitle>
<DrawerDescription>
Swipe {direction} or use the close button.
</DrawerDescription>
</DrawerHeader>
<div class="flex-1 p-4">
<div class="bg-muted h-40 rounded-lg" />
</div>
<DrawerFooter>
<DrawerClose variant="outline">Close</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
))
}
</div>Nested
Place a complete Drawer inside another drawer’s content. Escape closes only
the front drawer, then returns focus and interaction to its parent.
Project settings
Review the project or open its advanced settings.
Advanced settings
This drawer is nested inside the project settings drawer.
---
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
---
<Drawer id="nested-drawer-example">
<DrawerTrigger variant="outline">Open project settings</DrawerTrigger>
<DrawerContent showSwipeHandle>
<DrawerHeader>
<DrawerTitle>Project settings</DrawerTitle>
<DrawerDescription>
Review the project or open its advanced settings.
</DrawerDescription>
</DrawerHeader>
<div class="flex-1 p-4">
<div class="bg-muted h-40 rounded-lg"></div>
</div>
<DrawerFooter>
<Drawer id="nested-drawer-child">
<DrawerTrigger>Open advanced settings</DrawerTrigger>
<DrawerContent showSwipeHandle>
<DrawerHeader>
<DrawerTitle>Advanced settings</DrawerTitle>
<DrawerDescription>
This drawer is nested inside the project settings drawer.
</DrawerDescription>
</DrawerHeader>
<div class="flex-1 p-4">
<div class="bg-muted h-32 rounded-lg"></div>
</div>
<DrawerFooter>
<DrawerClose variant="outline">Close advanced settings</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
<DrawerClose variant="outline">Close project settings</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>Non-modal
Pair a non-modal drawer with showOverlay={false} so controls outside the
drawer remain visible and interactive.
Quick settings
The rest of the page stays available while this drawer is open.
---
import { Button } from "@/components/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
---
<div class="flex flex-wrap gap-2">
<Drawer modal={false}>
<DrawerTrigger variant="outline">Open non-modal drawer</DrawerTrigger>
<DrawerContent showOverlay={false} showSwipeHandle>
<DrawerHeader>
<DrawerTitle>Quick settings</DrawerTitle>
<DrawerDescription>
The rest of the page stays available while this drawer is open.
</DrawerDescription>
</DrawerHeader>
<div class="flex-1 p-4">
<div class="bg-muted h-32 rounded-lg"></div>
</div>
<DrawerFooter>
<DrawerClose variant="outline">Close</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
<Button variant="outline">Outside action</Button>
</div>modal="trap-focus" keeps keyboard focus inside the drawer without making the
rest of the page inert. Also set showOverlay={false} when outside pointer
interaction should remain available.
Snap point
One optional snap point controls how much of the panel is visible when open. A
fraction uses the viewport size. Pixel numbers and px or rem strings are
also supported.
Review changes
This drawer opens at sixty percent of the viewport height.
---
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
---
<Drawer snapPoint={0.6}>
<DrawerTrigger variant="outline">Open at 60%</DrawerTrigger>
<DrawerContent showSwipeHandle class="h-[60dvh]">
<DrawerHeader>
<DrawerTitle>Review changes</DrawerTitle>
<DrawerDescription>
This drawer opens at sixty percent of the viewport height.
</DrawerDescription>
</DrawerHeader>
<div class="flex-1 p-4">
<div class="bg-muted h-48 rounded-lg"></div>
</div>
<DrawerFooter>
<DrawerClose variant="outline">Close</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>Props and events
Drawer accepts defaultOpen, modal, disablePointerDismissal,
closeOnEscape, swipeDirection, snapPoint, defaultSnapPoint, triggerId,
and defaultTriggerId. DrawerContent accepts showOverlay,
showSwipeHandle, container, keepMounted, initialFocus, and finalFocus.
Use the drawer:open, drawer:close, drawer:toggle, and drawer:set DOM events
for imperative changes. The root emits cancelable drawer:beforechange and
drawer:beforesnapchange events, followed by drawer:change,
drawer:change-complete, and drawer:snapchange.
const drawer = document.querySelector('[data-slot="drawer"]')
drawer?.dispatchEvent(
new CustomEvent("drawer:set", {
detail: { open: true, snapPoint: "24rem" },
})
)
API Reference
See the GitHub source code for component props. See the data-slot docs for controllers, events, focus management, detached triggers, nested drawers, and keyboard-aware viewports.