Drawer
Edge panel (left, right, top, bottom) that springs in and swipes away. Shares Dialog's focus trap, scroll lock and Esc handling. Sidebar uses it below md.
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger,} from "@/components/ui/drawer";export function CartDrawer({ items }: { items: string[] }) { return ( <Drawer> <DrawerTrigger variant="outline">Cart</DrawerTrigger> <DrawerContent side="right"> <DrawerHeader> <DrawerTitle>Your cart</DrawerTitle> <DrawerDescription>{items.length} items</DrawerDescription> </DrawerHeader> <DrawerBody> <ul> {items.map((i) => ( <li key={i}>{i}</li> ))} </ul> </DrawerBody> <DrawerFooter> <DrawerClose>Keep shopping</DrawerClose> <DrawerClose variant="default">Checkout</DrawerClose> </DrawerFooter> </DrawerContent> </Drawer> );}<Drawer>
children*ReactNodeDialogTriggerandDialogContent.defaultOpendefaultfalsebooleanOpen at first render (uncontrolled).
onOpenChange(open: boolean) => voidCalled on the trigger, Esc, backdrop click or
DialogClose.openbooleanControlled state; pair with
onOpenChange.
| Prop | Type | Default |
|---|---|---|
children*
| ReactNode | — |
defaultOpenOpen at first render (uncontrolled). | boolean | false |
onOpenChangeCalled on the trigger, Esc, backdrop click or | (open: boolean) => void | — |
openControlled state; pair with | boolean | — |
<DrawerTrigger>
childrenReactNodeLabel; its width is reserved so loading and success never resize the button.
confirmLabelReactNodeTwo-step action: first click swaps the label to this, second click runs
onClick.confirmTimeoutnumberMs before an armed confirm step resets. Also resets on blur or Esc.
loadingbooleanDisables the button and shows a spinner before the label.
loadingLabelReactNodeLabel shown while loading, with the spinner. Defaults to
children.size"default" | "sm" | "lg" | "icon"Height and padding; "icon" is square, so give it an
aria-label.statusButtonStatusIn-place feedback:
loadingspins,successswaps the label tosuccessLabel.successLabelReactNodeText shown on
status="success", with a check icon. Defaults to "Done".swapSwapDirectionWhere the old label slides out to on a swap. Default "left".
variant"default" | "secondary" | "outline" | "ghost" | "destructive"Fill and emphasis: one primary action per view,
destructivefor irreversible ones.
| Prop | Type | Default |
|---|---|---|
childrenLabel; its width is reserved so loading and success never resize the button. | ReactNode | — |
confirmLabelTwo-step action: first click swaps the label to this, second click runs | ReactNode | — |
confirmTimeoutMs before an armed confirm step resets. Also resets on blur or Esc. | number | — |
loadingDisables the button and shows a spinner before the label. | boolean | — |
loadingLabelLabel shown while loading, with the spinner. Defaults to | ReactNode | — |
sizeHeight and padding; "icon" is square, so give it an | "default" | "sm" | "lg" | "icon" | — |
statusIn-place feedback: | ButtonStatus | — |
successLabelText shown on | ReactNode | — |
swapWhere the old label slides out to on a swap. Default "left". | SwapDirection | — |
variantFill and emphasis: one primary action per view, | "default" | "secondary" | "outline" | "ghost" | "destructive" | — |
Also accepts every Button prop.
<DrawerClose>
childrenReactNodeLabel; its width is reserved so loading and success never resize the button.
confirmLabelReactNodeTwo-step action: first click swaps the label to this, second click runs
onClick.confirmTimeoutnumberMs before an armed confirm step resets. Also resets on blur or Esc.
loadingbooleanDisables the button and shows a spinner before the label.
loadingLabelReactNodeLabel shown while loading, with the spinner. Defaults to
children.size"default" | "sm" | "lg" | "icon"Height and padding; "icon" is square, so give it an
aria-label.statusButtonStatusIn-place feedback:
loadingspins,successswaps the label tosuccessLabel.successLabelReactNodeText shown on
status="success", with a check icon. Defaults to "Done".swapSwapDirectionWhere the old label slides out to on a swap. Default "left".
variantdefault"outline""default" | "secondary" | "outline" | "ghost" | "destructive"Fill and emphasis: one primary action per view,
destructivefor irreversible ones.
| Prop | Type | Default |
|---|---|---|
childrenLabel; its width is reserved so loading and success never resize the button. | ReactNode | — |
confirmLabelTwo-step action: first click swaps the label to this, second click runs | ReactNode | — |
confirmTimeoutMs before an armed confirm step resets. Also resets on blur or Esc. | number | — |
loadingDisables the button and shows a spinner before the label. | boolean | — |
loadingLabelLabel shown while loading, with the spinner. Defaults to | ReactNode | — |
sizeHeight and padding; "icon" is square, so give it an | "default" | "sm" | "lg" | "icon" | — |
statusIn-place feedback: | ButtonStatus | — |
successLabelText shown on | ReactNode | — |
swapWhere the old label slides out to on a swap. Default "left". | SwapDirection | — |
variantFill and emphasis: one primary action per view, | "default" | "secondary" | "outline" | "ghost" | "destructive" | "outline" |
Also accepts every Button prop.
<DrawerTitle>
Also accepts every <h2> prop (none of its own).
<DrawerDescription>
Also accepts every <p> prop (none of its own).
<DrawerContent>
Panel that slides in from an edge on a spring. Swipe toward the edge to dismiss: side drawers drag from anywhere, top/bottom drawers from the handle or header so their content can scroll.
children*ReactNodeHeader, body and footer.
showClosedefaultside === "left" || side === "right"booleanClose button in the corner. On by default for side drawers.
sidedefault"right"SideEdge it slides in from.
| Prop | Type | Default |
|---|---|---|
children*Header, body and footer. | ReactNode | — |
showCloseClose button in the corner. On by default for side drawers. | boolean | side === "left" || side === "right" |
sideEdge it slides in from. | Side | "right" |
<DrawerHeader>
Also a drag handle on top/bottom drawers.
Also accepts every <div> prop (none of its own).
<DrawerBody>
Scrolls when content is taller than the drawer.
Also accepts every <div> prop (none of its own).
<DrawerFooter>
Also accepts every <div> prop (none of its own).
- type Side
- "left" | "right" | "top" | "bottom"
* required. Every component also takes className.