Skip to content
gnosticaignostic-ui
Esc
↑↓navigate↵open⌘Jpreview
On this page

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>
Open preview in a new tab

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>
Open preview in a new tab

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>
Open preview in a new tab

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>
Open preview in a new tab

Props

ComponentPropDefaultUse
Drawerposition“bottom”

“bottom”, “top”, “left”, or “right”

DrawerdefaultOpenfalseOpen when the component starts
Drawermodaltrue

true, false, or “trap-focus”

DrawercloseOnEscapetrueAllow Escape to close the drawer
DrawerdisablePointerDismissalfalsePrevent outside clicks from closing it
DrawercloseOnMedia—Close when this CSS media query matches
DrawerPopupvariant“default”

“default”, “straight”, or “inset”

DrawerPopup

showBar / showCloseButton

falseShow the swipe bar or close button
DrawerPopupkeepMountedfalseKeep the closed portal at its destination
DrawerPopupcontainer“body”CSS selector for the portal destination
DrawerPopup

initialFocus / finalFocus

runtime defaultBoolean or CSS selector
DrawerPanelscrollabletrueUse native scrolling for long content
DrawerFootervariant“default”

Use “bare” to remove its border and background

DrawerMenuItemcloseOnSelectfalseClose when the item is selected
DrawerMenuItemvariant“default”

Use “destructive” for a destructive action

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.

Use Sheet for an edge panel without drawer gestures or Dialog for a centered task.

Was this page helpful?