Drawer ported
A temporary side surface for focused work.
Overlay and panel with a titled header that doubles as the drag handle, a close button, and a scrolling body. Built on Bits UI Dialog.
Loading demo…
Guidance
When to use
- Side panels for filters, settings, or record details that keep the page in context.
- Forms that are too long for a dialog but should not leave the current view.
- Panels from any edge, via side, with drag-to-dismiss on the header.
When not to use
- Use dialog for short decisions and confirmations.
- Use bottom-sheet for mobile-first sheets with snap points.
- Use popover for small anchored content that does not need a modal overlay.
pnpm dlx shadcn-svelte@latest add @arcui/drawerUsage
<script lang="ts">
import { Drawer, DrawerTrigger, DrawerContent, DrawerClose } from '$lib/components/drawer';
let open = $state(false);
</script>
<Drawer bind:open>
<DrawerTrigger class="btn-primary">Open Filters</DrawerTrigger>
<DrawerContent title="Project Filters" description="Narrow the list of active repositories.">
<div class="filters-body">
<p>Filter options and configuration settings go here.</p>
</div>
<div class="filters-footer">
<DrawerClose class="btn-secondary">Apply Filters</DrawerClose>
</div>
</DrawerContent>
</Drawer>Variants & Examples
Right Slide-over
Default edge placement for inspector panels, record detail views, and edit forms.
<DrawerContent title="Settings" side="right">
<!-- Content -->
</DrawerContent>Left Navigation Drawer
Ideal for navigation drawers and hierarchy trees on smaller viewports.
<DrawerContent title="Navigation" side="left">
<!-- Content -->
</DrawerContent>Bottom Panel
Full-width bottom panel anchored to the bottom edge with centered grab bar.
<DrawerContent title="Console Output" side="bottom">
<!-- Content -->
</DrawerContent>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | undefined | Controlled open state; supports two-way bind:open. |
defaultOpen | boolean | false | Initial state when uncontrolled. |
onOpenChange | (open: boolean) => void | – | Callback fired when the drawer opens or closes. |
title | string | – | Dialog title rendered in the drawer header, linked to Dialog.Title. |
description | string | – | Supporting text rendered under the title, linked to Dialog.Description. |
side | 'left' | 'right' | 'top' | 'bottom' | 'right' | Edge the panel attaches to and slides from. |
container | HTMLElement | null | null | Renders the drawer inside this container element instead of the document body. |
class | string | – | Additional CSS class applied to the drawer panel. |
Keyboard Interactions
| Key | Action |
|---|---|
Escape | Closes the drawer and returns focus to the trigger button. |
Tab / Shift+Tab | Traps and cycles focus within the drawer content while open. |
Accessibility
- Built on Bits UI Dialog: provides role="dialog", aria-modal="true", and focus management.
- Title and description are wired to Dialog.Title and Dialog.Description for screen readers.
- Header close button is explicitly labelled "Close drawer".
- Focus is trapped while open and restored to trigger on dismissal.
Motion
Phase 1 still-state: Panel animates from its designated edge using CSS keyframes (drawer-in / drawer-out) and backdrop blur overlay. Phase 2 wires gesture drag-to-dismiss on header, rubber-banding past resting boundary, and velocity fling physics.
Notes for AI
- Use for side panels with forms, filters, settings, or detail views that keep the page in context.
- Compose Drawer as the root with DrawerTrigger, DrawerContent, and DrawerClose.
- Control open with bind:open when the drawer needs to close after an async form submission.
Source of truth: registry/components/drawer/drawer.tsx