2

Drawer

Installation

pnpm dlx @mielui/svelte add drawer

Usage

Compose Root, Trigger, Portal, Overlay, and Content. Inside Content, add any combination of Handle, Header, Title, Description, Body, Footer, and Close. Regions are independent. Top and bottom panels span the viewport; constrain individual regions to keep their content readable. You can omit Handle for a plain swipeable panel, move Close into Header, or restyle Content with class. Always provide Title or an aria-label on Content.

Use bind:open or onOpenChange to control visibility. Set direction to bottom, top, left, or right. handleOnly restricts dragging to the handle; closeThreshold controls the dismissal distance.

Drawers are modal. Focus enters the panel and returns to the trigger when it closes. Escape and outside interaction dismiss it unless dismissible is false. Include a Close button for keyboard users.

Set nested on a drawer inside another drawer. Its backdrop appears above the parent. Closing it returns focus to its trigger in the parent panel. Native regions support bind:element. Content accepts focus and outside-interaction callbacks.

Handle supports dragging; use Close for keyboard dismissal. Scrollable content belongs in Body. Add data-vaul-no-drag to custom controls that need their own pointer gestures. Snap-point expansion is not part of the supported API.

Reduced motion removes opening and settling animations while preserving dragging. Use Sheet when the panel does not need swipe gestures.

Nested review

Open the details from the review panel. Close the details to return to the review; each drawer has its own handle and close action.

API reference

Drawer.Root

Prop Type Default
children Snippet<[]> | undefined —
direction "left" | "right" | "top" | "bottom" | undefined

Direction of the drawer. Can be `top` or `bottom`, `left`, `right`.

—
open Bindable boolean | undefined false
onOpenChange ((open: boolean) => void) | undefined —
closeThreshold number | undefined

Number between 0 and 1 that determines when the drawer should be closed. Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more.

—
handleOnly boolean | undefined

When `true` only allows the drawer to be dragged by the `<Drawer.Handle />` component.

—
dismissible boolean | undefined

When `false` dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the `open` prop, otherwise you won't be able to open/close the drawer.

true
repositionInputs boolean | undefined

When `true` Vaul will reposition inputs rather than scroll then into view if the keyboard is in the way. Setting it to `false` will fall back to the default browser behavior.

—
nested boolean | undefined false

Drawer.Body

Prop Type Default
children Snippet<[]> | undefined —
element Bindable HTMLDivElement | null | undefined null

Drawer.Close

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
element Bindable HTMLButtonElement | null | undefined null

Drawer.Content

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
onInteractOutside InteractOutsideEventHandler | undefined

Callback fired when an outside interaction event completes, which is either a `pointerup`, `mouseup`, or `touchend` event, depending on the user's input device.

—
interactOutsideBehavior InteractOutsideBehaviorType | undefined

Interact outside behavior type. `close`: Closes the element immediately. `defer-otherwise-close`: Delegates the action to the parent element. If no parent is found, it closes the element. `defer-otherwise-ignore`: Delegates the action to the parent element. If no parent is found, nothing is done. `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to an outside interaction.

—
restoreScrollDelay number | null | undefined

The delay in milliseconds before the scrollbar is restored after closing the dialog. This is only applicable when using the `child` snippet for custom transitions and `preventScroll` is `true`. You should set this to a value greater than the transition duration to prevent content from shifting during the transition.

—
onEscapeKeydown ((e: KeyboardEvent) => void) | undefined

Callback fired when escape is pressed.

—
escapeKeydownBehavior EscapeBehaviorType | undefined

Escape behavior type. `close`: Closes the element immediately. `defer-otherwise-close`: Delegates the action to its parent component that has an escape keydown handler. If no parent is found, it closes the element. `defer-otherwise-ignore`: Delegates the action to the parent element. If no parent is found, nothing is done. `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to an escape key press.

—
forceMount boolean | undefined

Whether to force mount the component.

—
preventOverflowTextSelection boolean | undefined

Passing `true` will prevent the overflow of text selection outside the element, provided the element is the top layer.

—
onOpenAutoFocus EventCallback | undefined

Event handler called when auto-focusing on open. Can be prevented.

—
onCloseAutoFocus EventCallback | undefined

Event handler called when auto-focusing on close. Can be prevented.

—
trapFocus boolean | undefined

Whether focus is trapped within the focus scope.

—
element Bindable HTMLDivElement | null | undefined null

Drawer.Description

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
element Bindable HTMLParagraphElement | null | undefined null

Drawer.Footer

Prop Type Default
children Snippet<[]> | undefined —
element Bindable HTMLDivElement | null | undefined null

Drawer.Handle

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
element Bindable HTMLDivElement | null | undefined null

Drawer.Header

Prop Type Default
children Snippet<[]> | undefined —
element Bindable HTMLDivElement | null | undefined null

Drawer.Overlay

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
forceMount boolean | undefined

Whether to force mount the component.

—
element Bindable HTMLDivElement | null | undefined null

Drawer.Portal

Prop Type Default
to PortalTarget | undefined

Where to portal the content to.

—
disabled boolean | undefined

Disable portalling and render the component inline

—
children Snippet<[]> | undefined

The children content to render within the portal.

—

Drawer.Title

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
level 1 | 2 | 3 | 4 | 5 | 6 | undefined

The heading level of the dialog title.

—
element Bindable HTMLHeadingElement | null | undefined null

Drawer.Trigger

Prop Type Default
children Snippet<[]> | undefined —
id string | undefined —
style string | StyleProperties | null | undefined —
element Bindable HTMLButtonElement | null | undefined null