Sidebar

Drawer component rendered via x-teleport below body. Click trigger slot to open; Escape / × / background click closes. side (left/right), size (sm/md/lg), backdrop are the 3 axes. Focus trap (x-trap.inert.noscroll) and role=dialog/aria-modal=true for a11y. backdrop=false runs pointer-events-none on the overlay and pointer-events-auto on the panel, so you can inspect or interact with the page behind (persistent inspector, tool palette).

1. Minimal: with a trigger slot (default = side=left, size=md, backdrop on)

<x-sidebar>
    <x-slot:trigger>
        <x-button>Open</x-button>
    </x-slot:trigger>
    <p>Drawer content</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b6f32b9b.window="open = true" x-on:close-sidebar-sidebar_6a7e9b6f32b9b.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Open</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[-100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[-100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 left-0 h-full w-80 bg-base-100 text-base-content border-r border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Drawer content
                </p>
</div>
</div>
</template>
</div>

2. Right side + lg size

<x-sidebar side="right" size="lg">
    <x-slot:trigger><x-button>Open details</x-button></x-slot:trigger>
    <p>Details panel</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b6f3a913.window="open = true" x-on:close-sidebar-sidebar_6a7e9b6f3a913.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Open details</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 right-0 h-full w-96 bg-base-100 text-base-content border-l border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Details panel
                </p>
</div>
</div>
</template>
</div>

3. No backdrop — for a persistent inspector / tool palette. The overlay is pointer-events-none and only the panel is pointer-events-auto, so you can operate the main page while the drawer stays open

<x-sidebar :backdrop="false">
    <x-slot:trigger><x-button>Inspector</x-button></x-slot:trigger>
    <p>Inspector content — the page behind stays interactive.</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b6f4262c.window="open = true" x-on:close-sidebar-sidebar_6a7e9b6f4262c.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Inspector</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50 pointer-events-none" x-cloak="">
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[-100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[-100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 left-0 h-full w-80 bg-base-100 text-base-content border-r border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto pointer-events-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Inspector content — the page behind stays interactive.
                </p>
</div>
</div>
</template>
</div>

4. Specify an id + open it from elsewhere via dispatch

<x-sidebar id="filters" side="right">
    <p>Filter UI</p>
</x-sidebar>

<x-button x-on:click="$dispatch('open-sidebar-filters')">Filters</x-button>
<div x-data="{ open: false }" x-on:open-sidebar-filters.window="open = true" x-on:close-sidebar-filters.window="open = false">
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 right-0 h-full w-80 bg-base-100 text-base-content border-l border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Filter UI
                </p>
</div>
</div>
</template>
</div><button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer" x-on:click="$dispatch('open-sidebar-filters')">Filters</button>

side=left / size=md / backdrop=true / closeOnBackdrop=true / escape=true — press the trigger inside each iframe to check the behavior

Left (default) — side=left, size=md, backdrop=true

Right — side=right, size=md, backdrop=true

no-backdrop — :backdrop="false" / the background stays interactive

with-content — a practical navigation example (logo + menu + user)

Prop list

  • id — string|null (default: sidebar_{uniqid()}). Specify this when opening/closing it externally via dispatch
  • side'left' | 'right' (default: 'left')
  • size'sm' (w-64) | 'md' (w-80) | 'lg' (w-96) (default: 'md')
  • backdrop — bool (default: true). false removes the translucent black dim + keeps the background interactive
  • closeOnBackdrop — bool (default: true). Only effective when backdrop=true
  • escape — bool (default: true). Closes on the Escape key

props: id, side='left', size='md', backdrop=true, closeOnBackdrop=true, escape=true / size: sm (w-64) / md (w-80, default) / lg (w-96) / a11y: role=dialog, aria-modal=true, x-trap.inert.noscroll / rendering: teleported under body via x-teleport / slide-in animation