Drawer
Astro edge panels with data-slot dragging and dismissal.
This Astro component uses data-slot for behavior and Coss UI for its style. It uses the current theme colors, rounded edges, a blurred backdrop, and a muted footer.
---
import {
Drawer,
DrawerTrigger,
DrawerPopup,
DrawerHeader,
DrawerTitle,
DrawerDescription,
DrawerPanel,
DrawerFooter,
DrawerClose,
} from "@/components/ui/drawer"
---
<Drawer>
<DrawerTrigger variant="outline">Open drawer</DrawerTrigger>
<DrawerPopup showBar showCloseButton>
<DrawerHeader>
<DrawerTitle>Your workspace</DrawerTitle>
<DrawerDescription>
Keep the tools you need within reach.
</DrawerDescription>
</DrawerHeader>
<DrawerPanel>
<p>
Swipe down to close this panel. You can also press Escape or select
Done.
</p>
</DrawerPanel>
<DrawerFooter>
<DrawerClose>Done</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>
Installation
bunx --bun shadcn@latest add @gnosticai/drawer
Usage
---
import {
Drawer, DrawerTrigger, DrawerPopup, DrawerHeader, DrawerTitle,
DrawerDescription, DrawerPanel, DrawerFooter, DrawerClose,
DrawerMenu, DrawerMenuItem,
} from "@/components/ui/drawer"
---
<Drawer position="right">
<DrawerTrigger>Open menu</DrawerTrigger>
<DrawerPopup variant="inset" showCloseButton>
<DrawerHeader>
<DrawerTitle>Menu</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<DrawerMenu aria-label="Site navigation">
<DrawerMenuItem as="a" href="/about" closeOnSelect>About</DrawerMenuItem>
<DrawerMenuItem as="a" href="/contact" closeOnSelect>Contact</DrawerMenuItem>
</DrawerMenu>
</DrawerPanel>
</DrawerPopup>
</Drawer>
Always include DrawerTitle. Use class="sr-only" to hide the title visually.
Use Astro’s as prop for link controls. DrawerMenuItem keeps the drawer open
unless closeOnSelect is set. Links keep their normal navigation behavior.
Positions and styles
The root sets both the screen edge and the swipe direction. DrawerPopup
includes the portal, backdrop, and viewport.
---
import {
Drawer, DrawerTrigger, DrawerPopup, DrawerHeader, DrawerTitle, DrawerDescription,
} from "@/components/ui/drawer"
---
<div class="flex flex-wrap gap-3">
<Drawer position="left">
<DrawerTrigger variant="outline">Left inset</DrawerTrigger>
<DrawerPopup variant="inset" showCloseButton>
<DrawerHeader>
<DrawerTitle>Left drawer</DrawerTitle>
<DrawerDescription>The inset style adds space on larger screens.</DrawerDescription>
</DrawerHeader>
</DrawerPopup>
</Drawer>
<Drawer position="right">
<DrawerTrigger variant="outline">Right</DrawerTrigger>
<DrawerPopup showCloseButton>
<DrawerHeader><DrawerTitle>Right drawer</DrawerTitle></DrawerHeader>
</DrawerPopup>
</Drawer>
<Drawer position="top">
<DrawerTrigger variant="outline">Top straight</DrawerTrigger>
<DrawerPopup variant="straight" showCloseButton>
<DrawerHeader><DrawerTitle>Top drawer</DrawerTitle></DrawerHeader>
</DrawerPopup>
</Drawer>
</div>
Scrollable content
The panel scrolls while the title and footer stay in place. Native scrolling keeps long menus usable on small screens.
---
import {
Drawer, DrawerTrigger, DrawerPopup, DrawerHeader, DrawerTitle,
DrawerPanel, DrawerFooter, DrawerClose,
} from "@/components/ui/drawer"
---
<Drawer>
<DrawerTrigger variant="outline">Long content</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader><DrawerTitle>Workspace notes</DrawerTitle></DrawerHeader>
<DrawerPanel>
<ol class="flex flex-col gap-4">
{Array.from({ length: 30 }, (_, i) => <li>Note {i + 1}: Keep each task clear and easy to find.</li>)}
</ol>
</DrawerPanel>
<DrawerFooter><DrawerClose>Done</DrawerClose></DrawerFooter>
</DrawerPopup>
</Drawer>
Nested drawers
Place a complete drawer inside the parent panel. Escape and swipe dismiss the front drawer first. Focus returns to its trigger.
---
import {
Drawer,
DrawerTrigger,
DrawerPopup,
DrawerHeader,
DrawerTitle,
DrawerPanel,
DrawerFooter,
DrawerClose,
} from "@/components/ui/drawer"
---
<Drawer>
<DrawerTrigger variant="outline">Nested drawers</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>Workspace</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<Drawer>
<DrawerTrigger variant="outline">Open details</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>Details</DrawerTitle>
</DrawerHeader>
<DrawerPanel>Swipe down to return to the workspace.</DrawerPanel>
<DrawerFooter>
<DrawerClose>Back</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>
</DrawerPanel>
<DrawerFooter>
<DrawerClose>Done</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>
Props
| Component | Prop | Default | Use |
|---|---|---|---|
| Drawer | position | “bottom” |
|
| Drawer | defaultOpen | false | Open when the component starts |
| Drawer | modal | true |
|
| Drawer | closeOnEscape | true | Allow Escape to close the drawer |
| Drawer | disablePointerDismissal | false | Prevent outside clicks from closing it |
| Drawer | closeOnMedia | — | Close when this CSS media query matches |
| DrawerPopup | variant | “default” |
|
| DrawerPopup |
| false | Show the swipe bar or close button |
| DrawerPopup | keepMounted | false | Keep the closed portal at its destination |
| DrawerPopup | container | “body” | CSS selector for the portal destination |
| DrawerPopup |
| runtime default | Boolean or CSS selector |
| DrawerPanel | scrollable | true | Use native scrolling for long content |
| DrawerFooter | variant | “default” | Use |
| DrawerMenuItem | closeOnSelect | false | Close when the item is selected |
| DrawerMenuItem | variant | “default” | Use |
These wrappers provide full-size drawers. Coss React props such as render,
onOpenChange, and multi-point snapPoints are not Astro props. For application
state, use data-slot’s drawer:open, drawer:close, and drawer:change DOM events.
For a complete navigation block, see Mobile Menu.
When to use Drawer
Use Drawer for a short task or detail view that enters from a screen edge. Keep a title and explicit close control. Choose the direction that fits the page; a bottom drawer can work well for short mobile actions.
Keyboard and behavior
The current controller handles dragging, Escape, and modal focus. Keep a close button available so dragging is never the only way out. Multi-point Coss snap positions are not part of this Astro API.
Theme and layout
The drawer uses shared dialog colors and direction-specific motion. Test long content while dragging and scrolling on touch screens. A larger drawer needs a constrained body so controls remain reachable.
Related choices
Use Sheet for an edge panel without drawer gestures or Dialog for a centered task.