VayuUI

Drawer

A sidebar panel that slides in from any edge of the viewport for navigation, forms, or supplementary content.

Installation

npx vayu-ui init #one time only
npx vayu-ui add drawer
npx vayu-ui add -t drawer #with test case needs

Usage

Drawer Example

<Drawer side="right">
  <Drawer.Trigger asChild>
    <Button>Open Drawer</Button>
  </Drawer.Trigger>
  <Drawer.Overlay />
  <Drawer.Content>
    <Drawer.Header>
      <Drawer.Title>Edit Profile</Drawer.Title>
      <Drawer.Description>Make changes to your profile here.</Drawer.Description>
    </Drawer.Header>
    <div className="py-4">
      Profile form content...
    </div>
    <Drawer.Footer>
      <Drawer.Close asChild>
        <Button>Save</Button>
      </Drawer.Close>
    </Drawer.Footer>
  </Drawer.Content>
</Drawer>

<Drawer side="left">
  <Drawer.Trigger asChild>
    <Button>Open Navigation</Button>
  </Drawer.Trigger>
  <Drawer.Overlay />
  <Drawer.Content>
    <Drawer.Header>
      <Drawer.Title>Navigation</Drawer.Title>
      <Drawer.Description>Site navigation links.</Drawer.Description>
    </Drawer.Header>
    <nav className="py-4">
      Navigation items...
    </nav>
  </Drawer.Content>
</Drawer>

Anatomy

import { Drawer } from 'vayu-ui';

<Drawer side="right">
  <Drawer.Trigger asChild>
    <Button>Open</Button>
  </Drawer.Trigger>
  <Drawer.Overlay />
  <Drawer.Content>
    <Drawer.Header>
      <Drawer.Title>Title</Drawer.Title>
      <Drawer.Description>Description goes here.</Drawer.Description>
    </Drawer.Header>
    <div className="py-4">{/* Content */}</div>
    <Drawer.Footer>
      <Drawer.Close asChild>
        <Button>Close</Button>
      </Drawer.Close>
    </Drawer.Footer>
  </Drawer.Content>
</Drawer>;
  • Drawer — Root container. Manages open/close state and provides context to all subcomponents. Supports controlled and uncontrolled usage.
  • Drawer.Trigger — Button that opens the drawer. Supports asChild to merge props onto a custom element.
  • Drawer.Overlay — Backdrop behind the drawer. Clicking it dismisses the drawer by default.
  • Drawer.Content — The slide-in panel with focus trapping and keyboard navigation. Includes a built-in close button.
  • Drawer.Header — Container for Drawer.Title and Drawer.Description.
  • Drawer.Title — Accessible heading rendered as an <h2>. Linked to the dialog via aria-labelledby.
  • Drawer.Description — Accessible description rendered as a <p>. Linked to the dialog via aria-describedby.
  • Drawer.Footer — Container for action buttons, pinned to the bottom of the drawer.
  • Drawer.Close — Button that closes the drawer. Supports asChild for custom elements.
  • Drawer.Portal — Portal wrapper for rendering the drawer outside the DOM tree.

Accessibility

  • Keyboard Support:
    • Escape — Closes the drawer.
    • Tab — Moves focus between interactive elements inside the drawer.
    • Shift + Tab — Moves focus backwards through interactive elements.
    • Focus is trapped within the drawer when open (circular tab navigation).
  • ARIA Attributes:
    • role="dialog" on Drawer.Content.
    • aria-modal="true" on Drawer.Content.
    • aria-labelledby on Drawer.Content links to Drawer.Title.
    • aria-describedby on Drawer.Content links to Drawer.Description.
    • aria-expanded on Drawer.Trigger reflects the open/closed state.
    • aria-haspopup="dialog" on Drawer.Trigger.
    • aria-hidden="true" on Drawer.Overlay.
    • Built-in close button has aria-label="Close drawer".
  • Focus Behavior:
    • When the drawer opens, focus moves to the first interactive element inside Drawer.Content.
    • Focus is trapped within the drawer until it closes.
    • When the drawer closes, focus returns to the trigger element.
    • Body scroll is locked while a modal drawer is open.

Screen reader behavior

When a drawer opens, the screen reader announces the dialog (via role="dialog" and aria-modal="true") and reads the title (linked by aria-labelledby) and description (linked by aria-describedby). The overlay is hidden from the assistive technology tree (aria-hidden="true"). While the drawer is open, only content inside it is accessible to screen readers. When the drawer closes, focus returns to the trigger button and the previously visible content becomes accessible again.

Component Folder Structure

Drawer/
├── Drawer.tsx             # Root component with context provider and state management
├── DrawerTrigger.tsx      # Trigger button that opens the drawer
├── DrawerOverlay.tsx      # Modal backdrop with dismiss on click
├── DrawerContent.tsx      # Slide-in panel with focus trapping and keyboard navigation
├── DrawerHeader.tsx       # Container for title and description
├── DrawerFooter.tsx       # Container for action buttons
├── DrawerTitle.tsx        # Accessible heading linked to dialog
├── DrawerDescription.tsx  # Accessible description linked to dialog
├── DrawerClose.tsx        # Button that closes the drawer
├── DrawerPortal.tsx       # Portal wrapper for DOM rendering
├── types.ts               # TypeScript type definitions
├── index.ts               # Re-exports all components and types
└── README.md              # Component usage reference

Props

Drawer (Root)

PropTypeDefaultDescription
openbooleanControlled open state.
onOpenChange(open: boolean) => voidCallback when open state changes.
defaultOpenbooleanfalseDefault open state for uncontrolled usage.
side"left" | "right" | "top" | "bottom""right"Direction the drawer slides in from.
modalbooleantrueWhether to lock body scroll and show overlay.
childrenReactNodeDrawer subcomponents.
classNamestringAdditional CSS classes.

Drawer.Trigger

PropTypeDefaultDescription
asChildbooleanfalseMerge props onto child element instead of rendering a button.
classNamestringAdditional CSS classes.

Drawer.Overlay

PropTypeDefaultDescription
dismissiblebooleantrueWhether clicking the overlay closes the drawer.
classNamestringAdditional CSS classes.

Drawer.Content

PropTypeDefaultDescription
trapFocusbooleantrueWhether to trap focus within the drawer when open.
classNamestringAdditional CSS classes.

Drawer.Header

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Drawer.Footer

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Drawer.Title

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Drawer.Description

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Drawer.Close

PropTypeDefaultDescription
asChildbooleanfalseMerge props onto child element instead of rendering a button.
classNamestringAdditional CSS classes.

Drawer.Portal

PropTypeDefaultDescription
childrenReactNodeContent to render inside the portal.

On this page