Drawer

Slide-out panel for additional content and actions

Drawer

The Drawer component provides a slide-out panel from the bottom of the screen for displaying additional content, forms, or actions without leaving the current context.

Installation

Add the drawer component to your project:

npx velyx add drawer
pnpm dlx velyx add drawer
yarn dlx velyx add drawer
bunx --bun velyx add drawer

Alpine.js Required: The drawer component requires Alpine.js for interactivity. Make sure Alpine.js is installed in your project.

Usage

Basic Drawer

Loading preview...

Failed to load preview

Code

Without Overlay

Loading preview...

Failed to load preview

Code

With Form

Loading preview...

Failed to load preview

Code

Interactive Example

Loading preview...

Failed to load preview

Code

Components

The Drawer component consists of several sub-components:

Component Purpose
<x-ui.drawer> Main drawer container with Alpine.js state management
<x-ui.drawer.trigger> Button that opens the drawer
<x-ui.drawer.content> The slide-out panel content
<x-ui.drawer.header> Header section with title and description
<x-ui.drawer.title> Drawer title
<x-ui.drawer.description> Optional description below title
<x-ui.drawer.footer> Footer section for action buttons
<x-ui.drawer.close> Button that closes the drawer

Props

Drawer Root

Prop Type Default Description
open boolean false Whether the drawer is initially open
closeOnEscape boolean true Whether pressing Escape closes the drawer

Drawer Content

Prop Type Default Description
showOverlay boolean true Whether to show the dark backdrop overlay

Drawer Trigger

Prop Type Default Description
target string required ID of the drawer to open
asChild boolean false Whether to render as child element instead of button

Drawer Close

Prop Type Default Description
target string required ID of the drawer to close
asChild boolean false Whether to render as child element instead of button

Examples

Basic Drawer Structure

<x-ui.drawer id="my-drawer">
    <x-ui.drawer.trigger target="my-drawer">
        <x-ui.button>Open</x-ui.button>
    </x-ui.drawer.trigger>

    <x-ui.drawer.content>
        <div class="mx-auto w-full max-w-sm">
            <x-ui.drawer.header>
                <x-ui.drawer.title>Title</x-ui.drawer.title>
            </x-ui.drawer.header>

            <div class="p-4">
                Your content here
            </div>
        </div>
    </x-ui.drawer.content>
</x-ui.drawer>

With Custom Trigger

<x-ui.drawer id="settings-drawer">
    <x-ui.drawer.trigger as-child="true" target="settings-drawer">
        <button class="px-4 py-2 bg-blue-500 text-white rounded">
            Open Settings
        </button>
    </x-ui.drawer.trigger>

    <x-ui.drawer.content>
        <!-- content -->
    </x-ui.drawer.content>
</x-ui.drawer>

Controlled with Alpine.js

<div x-data="{ open: false }">
    <button @click="$dispatch('drawer-open', { drawerId: 'my-drawer' })">
        Open Drawer
    </button>

    <x-ui.drawer id="my-drawer">
        <x-ui.drawer.content>
            <div class="p-4">
                Drawer content
            </div>
        </x-ui.drawer.content>
    </x-ui.drawer>
</div>

With Livewire

<div x-data="{ open: @entangle($drawerOpen).live }">
    <button @click="open = true">Toggle</button>

    <x-ui.drawer :open="open" id="livewire-drawer">
        <x-ui.drawer.content>
            <form wire:submit="save">
                <!-- form fields -->
            </form>
        </x-ui.drawer.content>
    </x-ui.drawer>
</div>

Multiple Drawers

<x-ui.drawer id="drawer-1">
    <x-ui.drawer.trigger target="drawer-1">
        <x-ui.button>Open Drawer 1</x-ui.button>
    </x-ui.drawer.trigger>
    <!-- content -->
</x-ui.drawer>

<x-ui.drawer id="drawer-2">
    <x-ui.drawer.trigger target="drawer-2">
        <x-ui.button>Open Drawer 2</x-ui.button>
    </x-ui.drawer.trigger>
    <!-- content -->
</x-ui.drawer>

Without Close on Escape

<x-ui.drawer :close-on-escape="false" id="important-drawer">
    <x-ui.drawer.content>
        <div class="p-4">
            <p>This drawer requires explicit action to close.</p>
            <x-ui.drawer.close target="important-drawer">
                <x-ui.button>Close</x-ui.button>
            </x-ui.drawer.close>
        </div>
    </x-ui.drawer.content>
</x-ui.drawer>

Accessibility

The Drawer component includes proper ARIA attributes and keyboard support:

  • Focus Management: Focus is trapped within the drawer when open
  • Escape Key: Closes the drawer by default (can be disabled)
  • Click Outside: Clicking the overlay closes the drawer
  • Scroll Lock: Body scroll is locked when drawer is open
  • Screen Reader: Proper ARIA roles and labels
  • Teleport: Content is teleported to body for proper z-index layering

Styling

The drawer uses Tailwind CSS utility classes:

  • Overlay: bg-black/50 with fade transitions
  • Content: fixed inset-x-0 bottom-0 for bottom sheet style
  • Handle: h-2 w-[100px] drag indicator at top
  • Animation: Slide up from bottom with translate-y

Notes

  • The drawer animates from the bottom of the screen (bottom sheet style)
  • Click outside the drawer or press Escape to close
  • The handle at the top provides a visual indicator for swipe gestures
  • Content is automatically teleported to the body element
  • Multiple drawers can be used on the same page with unique IDs
  • Body scroll is locked when the drawer is open

Next Steps