

# Drawer

Slide-over panel for secondary flows (settings, details, filters). Built on React Aria Modal with focus trap, Escape, and backdrop dismissal. Control open state with `isOpen` / `onOpenChange`. The default export stays the assembled preset (`isOpen` / `title` / children). Compound parts (`Drawer.Root`, `Drawer.Backdrop`, `Drawer.Panel`, `Drawer.Header`, `Drawer.Title`, `Drawer.Close`, `Drawer.Body`) are available for custom chrome. There is no `Drawer.Trigger`. A `Drawer.Close` child must forward DOM props (a `Button` or a host element) — there is no `render` prop.

State attributes, exit animation, and `onOpenChange` details are documented in [Overlay lifecycle](/docs/overlay-lifecycle).

## Examples

### Default

Open from a button; `placement` defaults to `end`.

```tsx
import { Button, Drawer } from '@var-ui/react';

<Button onPress={() => setOpen(true)}>Open drawer</Button>
<Drawer isOpen={open} onOpenChange={setOpen} title="Settings">
  <p>Drawer body content.</p>
</Drawer>
```

```astro
<!-- No Astro binding yet — use @var-ui/react -->
```

```html
<!-- No HTML demo yet — use @var-ui/react -->
```

### Placement

Use `placement` (`start`, `end`, or `bottom`) to match the layout edge that owns the flow.

```tsx
import { Button, Drawer } from '@var-ui/react';
import { useState } from 'react';

const [open, setOpen] = useState(false);

<Button intent="secondary" onPress={() => setOpen(true)}>
  Open from start
</Button>
<Drawer
  isOpen={open}
  onOpenChange={setOpen}
  title="Filters"
  placement="start"
>
  <p>Panel slides in from the start edge.</p>
</Drawer>
```

```astro
<!-- No Astro binding yet — use @var-ui/react -->
```

```html
<!-- No HTML demo yet — use @var-ui/react -->
```

## Props

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| children | ReactNode | no |  | Drawer body content. |
| className | (ClassNameOrFunction<ModalRenderProps> & string) \| undefined | no | 'react-aria-ModalOverlay' | The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. |
| closeLabel | string \| undefined | no | 'Close' | Label for the close button. |
| height | number \| undefined | no |  | Custom panel height in px (overrides size height for bottom). |
| label | string \| undefined | no | 'Drawer' | Accessible name when `title` is omitted. |
| onOpenChange | OverlayOpenChangeHandler \| undefined | no |  |  |
| placement | "bottom" \| "end" \| "start" \| undefined | no | 'end' | Edge the panel slides in from. |
| portalContainer | Element \| undefined | no |  | Element the drawer portals into instead of `document.body`. Needed when a subtree renders under a different theme than the page ambient. |
| title | ReactNode | no |  | Panel title rendered in the header when `title` is set. |
| width | number \| undefined | no |  | Custom panel width in px (overrides size width for start/end). |
| dir | string \| undefined | no |  |  |
| hidden | boolean \| undefined | no |  |  |
| inert | boolean \| undefined | no |  |  |
| isDismissable | boolean \| undefined | no | false | Whether to close the modal when the user interacts outside it. |
| isEntering | boolean \| undefined | no |  | Whether the modal is currently performing an entry animation. |
| isExiting | boolean \| undefined | no |  | Whether the modal is currently performing an exit animation. |
| isKeyboardDismissDisabled | boolean \| undefined | no | false | Whether pressing the escape key to close the modal should be disabled. |
| lang | string \| undefined | no |  |  |
| onAnimationEnd | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAnimationEndCapture | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAnimationIteration | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAnimationIterationCapture | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAnimationStart | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAnimationStartCapture | AnimationEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAuxClick | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onAuxClickCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onClick | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onClickCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onContextMenu | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onContextMenuCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onDoubleClick | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onDoubleClickCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onGotPointerCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onGotPointerCaptureCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onLostPointerCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onLostPointerCaptureCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseDown | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseDownCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseEnter | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseLeave | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseMove | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseMoveCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseOut | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseOutCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseOver | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseOverCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseUp | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onMouseUpCapture | MouseEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerCancel | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerCancelCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerDown | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerDownCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerEnter | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerLeave | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerMove | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerMoveCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerOut | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerOutCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerOver | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerOverCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerUp | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onPointerUpCapture | PointerEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onScroll | UIEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onScrollCapture | UIEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchCancel | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchCancelCapture | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchEnd | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchEndCapture | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchMove | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchMoveCapture | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchStart | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTouchStartCapture | TouchEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionCancel | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionCancelCapture | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionEnd | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionEndCapture | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionRun | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionRunCapture | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionStart | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onTransitionStartCapture | TransitionEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onWheel | WheelEventHandler<HTMLDivElement> \| undefined | no |  |  |
| onWheelCapture | WheelEventHandler<HTMLDivElement> \| undefined | no |  |  |
| render | DOMRenderFunction<"div", ModalRenderProps> \| undefined | no |  | Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components.  Requirements:  - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an   `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop   as appropriate. |
| shouldCloseOnInteractOutside | ((element: Element) => boolean) \| undefined | no |  | When user interacts with the argument element outside of the overlay ref, return true if onClose should be called.  This gives you a chance to filter out interaction with elements that should not dismiss the overlay. By default, onClose will always be called on interaction outside the overlay ref. |
| slot | string \| null \| undefined | no |  | A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent. |
| style | StyleOrFunction<ModalRenderProps> \| undefined | no |  | The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state. |
| translate | "yes" \| "no" \| undefined | no |  |  |
| UNSTABLE_portalContainer | Element \| undefined | no | document.body | The container element in which the overlay portal will be placed. This may have unknown behavior depending on where it is portalled to. |
| defaultOpen | boolean \| undefined | no |  | Whether the overlay is open by default (uncontrolled). |
| isOpen | boolean \| undefined | no |  | Whether the overlay is open by default (controlled). |
| size | DrawerSize \| undefined | no |  |  |

## Accessibility

Focus moves into the panel when opened and returns to the trigger on close. Provide `title` or `label` for an accessible name. The close control defaults to `aria-label="Close"`. Prefer drawers for multi-step or longer secondary UI; use `AlertDialog` for short confirmations.
