HeroUI

DrawerNew

Slide-out panel for supplementary content and actions

Import

import { Drawer, Button } from "@heroui/react";

Usage

import {Button, Drawer} from "@heroui/react";

export function Basic() {
  return (
    <Drawer>

Anatomy

import { Drawer, Button } from "@heroui/react";

export default () => (
  <Drawer>
    <Button>Open Drawer</Button>
    <Drawer.Backdrop>
      <Drawer.Content>
        <Drawer.Dialog>
          <Drawer.Handle /> {/* Optional: Drag handle */}
          <Drawer.CloseTrigger /> {/* Optional: Close button */}
          <Drawer.Header>
            <Drawer.Heading />
          </Drawer.Header>
          <Drawer.Body />
          <Drawer.Footer />
        </Drawer.Dialog>
      </Drawer.Content>
    </Drawer.Backdrop>
  </Drawer>
);

Placement

import {Button, Drawer} from "@heroui/react";

export function Placements() {
  const placements = ["bottom", "top", "left", "right"] as const;

Backdrop Variants

import {Button, Drawer} from "@heroui/react";

export function BackdropVariants() {
  const variants = ["opaque", "blur", "transparent"] as const;

Non-Dismissable

Set isDismissable={false} on Drawer.Backdrop to prevent closing by clicking outside or dragging. The user must interact with the drawer's action buttons.

import {Button, Drawer} from "@heroui/react";

export function NonDismissable() {
  return (
    <Drawer>

Scrollable Content

The Drawer.Body automatically handles overflow with native scrolling. Drag-to-dismiss is excluded from the body area to avoid scroll conflicts.

import {Button, Drawer} from "@heroui/react";

export function ScrollableContent() {
  return (
    <Drawer>

Controlled State

With React.useState()

Control the drawer using React's useState hook for simple state management.

Status: closed

With useOverlayState()

Use the useOverlayState hook for a cleaner API with convenient methods like open(), close(), and toggle().

Status: closed

"use client";

import {Button, Drawer, useOverlayState} from "@heroui/react";
import React from "react";

With Form

import {Button, Drawer, Input, Label, TextField} from "@heroui/react";

export function WithForm() {
  return (
    <Drawer>
import type {ComponentType, SVGProps} from "react";

import {Bars, Bell, Envelope, Gear, House, Magnifier, Person} from "@gravity-ui/icons";
import {Button, Drawer} from "@heroui/react";

Styling

Passing Tailwind CSS classes

import { Drawer, Button } from "@heroui/react";

function CustomDrawer() {
  return (
    <Drawer>
      <Button>Open Drawer</Button>
      <Drawer.Backdrop className="bg-black/80">
        <Drawer.Content>
          <Drawer.Dialog className="bg-linear-to-br from-purple-500 to-pink-500 text-white">
            <Drawer.CloseTrigger />
            <Drawer.Header>
              <Drawer.Heading>Custom Styled Drawer</Drawer.Heading>
            </Drawer.Header>
            <Drawer.Body>
              <p>This drawer has custom styling applied via Tailwind classes.</p>
            </Drawer.Body>
            <Drawer.Footer>
              <Button slot="close">Close</Button>
            </Drawer.Footer>
          </Drawer.Dialog>
        </Drawer.Content>
      </Drawer.Backdrop>
    </Drawer>
  );
}

Customizing the component classes

To customize the Drawer component classes, you can use the @layer components directive.


Learn more.

@layer components {
  .drawer__backdrop {
    @apply bg-gradient-to-br from-black/50 to-black/70;
  }

  .drawer__dialog {
    @apply rounded-2xl border border-white/10 shadow-2xl;
  }

  .drawer__header {
    @apply text-center;
  }

  .drawer__close-trigger {
    @apply rounded-full bg-white/10 hover:bg-white/20;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Drawer component uses these CSS classes (View source styles):

Base Classes

  • .drawer__trigger - Trigger element that opens the drawer
  • .drawer__backdrop - Overlay backdrop behind the drawer
  • .drawer__content - Positioning wrapper for the drawer panel
  • .drawer__dialog - The drawer panel itself
  • .drawer__header - Header section for titles
  • .drawer__heading - Main title text
  • .drawer__body - Main scrollable content area
  • .drawer__footer - Footer section for actions
  • .drawer__handle - Visual drag handle indicator
  • .drawer__close-trigger - Close button element

Backdrop Variants

  • .drawer__backdrop--opaque - Opaque colored backdrop (default)
  • .drawer__backdrop--blur - Blurred backdrop with glass effect
  • .drawer__backdrop--transparent - Transparent backdrop (no overlay)

Placement Variants

  • .drawer__content--bottom - Slides up from the bottom edge (default)
  • .drawer__content--top - Slides down from the top edge
  • .drawer__content--left - Slides in from the left edge
  • .drawer__content--right - Slides in from the right edge

Dialog Variants

  • .drawer__dialog--top - Slides down from the top edge
  • .drawer__dialog--bottom - Slides up from the bottom edge
  • .drawer__dialog--left - Slides in from the left edge
  • .drawer__dialog--right - Slides in from the right edge

Interactive States

The component supports these interactive states:

  • Focus: :focus-visible or [data-focus-visible="true"] - Applied to trigger and close button
  • Hover: :hover or [data-hovered="true"] - Applied to close button on hover
  • Active: :active or [data-pressed="true"] - Applied to trigger and close button when pressed
  • Entering: [data-entering] - Applied during drawer opening animation
  • Exiting: [data-exiting] - Applied during drawer closing animation
  • Placement: [data-placement="*"] - Applied based on drawer position (top, bottom, left, right)

API Reference

Drawer

PropTypeDefaultDescription
childrenReactNode-Trigger and backdrop elements
stateUseOverlayStateReturn-Controlled overlay state

Drawer.Trigger

PropTypeDefaultDescription
childrenReactNode-Custom trigger content
classNamestring-CSS classes

Drawer.Backdrop

PropTypeDefaultDescription
variant"opaque" | "blur" | "transparent""opaque"Backdrop overlay style
isDismissablebooleantrueClose on backdrop click
isKeyboardDismissDisabledbooleanfalseDisable ESC key to close
isOpenboolean-Controlled open state
onOpenChange(isOpen: boolean) => void-Open state change handler
classNamestring | (values) => string-Backdrop CSS classes

Drawer.Content

PropTypeDefaultDescription
placement"top" | "bottom" | "left" | "right""bottom"Edge the drawer slides from
classNamestring | (values) => string-Content CSS classes

Drawer.Dialog

PropTypeDefaultDescription
childrenReactNode-Dialog content
classNamestring-CSS classes
rolestring"dialog"ARIA role
aria-labelstring-Accessibility label
aria-labelledbystring-ID of label element

Drawer.Header

PropTypeDefaultDescription
childrenReactNode-Header content
classNamestring-CSS classes

Drawer.Heading

PropTypeDefaultDescription
childrenReactNode-Title text
classNamestring-CSS classes

Drawer.Body

PropTypeDefaultDescription
childrenReactNode-Body content
classNamestring-CSS classes

Drawer.Footer

PropTypeDefaultDescription
childrenReactNode-Footer content
classNamestring-CSS classes

Drawer.Handle

PropTypeDefaultDescription
classNamestring-CSS classes

Drawer.CloseTrigger

PropTypeDefaultDescription
childrenReactNode-Custom close button
classNamestring | (values) => string-CSS classes

useOverlayState Hook

import { useOverlayState } from "@heroui/react";

const state = useOverlayState({
  defaultOpen: false,
  onOpenChange: (isOpen) => console.log(isOpen),
});

state.isOpen; // Current state
state.open(); // Open drawer
state.close(); // Close drawer
state.toggle(); // Toggle state
state.setOpen(); // Set state directly

Accessibility

Implements WAI-ARIA Dialog pattern:

  • Focus trap: Focus locked within drawer when open
  • Keyboard: ESC closes (when dismissable), Tab cycles elements
  • Screen readers: Proper ARIA attributes via React Aria
  • Scroll lock: Body scroll disabled when open
  • Drag to dismiss: Supports pointer-based drag gestures on handle, header, and footer areas

On this page