Skip to documentation
BoobstrapDocs
Components

Drawers

Open full-height panels from either logical edge with custom widths, fixed regions, scrollable bodies, and optional backdrop dismissal.

Components guideCopy-ready referencev0.7.0 current
Components

Drawers

Drawers use the same native dialog controller and lifecycle as modals, but fill the viewport height and enter from the logical start or end edge. Their optional header and footer remain visible while the body scrolls.

Drawer versus sidebar: use a drawer for a temporary task or contextual panel at any viewport. Use the Sidebar component for persistent application navigation that becomes a drawer only at responsive breakpoints.

End drawer with a form

The common task-panel pattern opens from the logical end edge, keeps its context and actions visible, and protects unsaved form work from accidental backdrop clicks.

Account settings

Manage workspace identity and contact details.

HTML · End drawer form
<button class="bs-btn bs-btn-primary" type="button" data-bs-toggle="dialog" aria-controls="account-drawer">Edit account</button>
<dialog class="bs-drawer bs-drawer-end" id="account-drawer" data-bs-dialog data-bs-dialog-close-on-backdrop="false" aria-labelledby="account-drawer-title" aria-describedby="account-drawer-description">
  <header class="bs-drawer-header">
    <h4 class="bs-drawer-title" id="account-drawer-title">Account settings</h4>
    <p class="bs-drawer-description" id="account-drawer-description">Manage workspace identity and contact details.</p>
    <button class="bs-drawer-close" type="button" data-bs-dialog-dismiss aria-label="Close account settings">&times;</button>
  </header>
  <div class="bs-drawer-body">
    <form class="bs-stack bs-gap-4"><div class="bs-form-group"><label class="bs-label" for="drawer-company">Company</label><input class="bs-input" id="drawer-company" value="Analytical Engines" /></div><div class="bs-form-group"><label class="bs-label" for="drawer-email">Contact email</label><input class="bs-input" id="drawer-email" type="email" value="ada@example.com" /></div><label class="bs-check"><input class="bs-check-input" type="checkbox" checked /><span class="bs-check-label">Product updates</span><span class="bs-check-description">Receive occasional release notes.</span></label></form>
  </div>
  <footer class="bs-drawer-footer"><button class="bs-btn bs-btn-secondary" type="button" data-bs-dialog-dismiss>Cancel</button><button class="bs-btn bs-btn-primary" type="button">Save</button></footer>
</dialog>

Logical placement follows document direction. Omit the footer when controls apply immediately and the drawer needs no persistent bottom action.

Filters

Status
HTML · Start drawer
<button class="bs-btn bs-btn-secondary" type="button" data-bs-toggle="dialog" aria-controls="filter-drawer">Filter results</button>
<dialog class="bs-drawer bs-drawer-start bs-drawer-sm" id="filter-drawer" data-bs-dialog aria-labelledby="filter-drawer-title">
  <header class="bs-drawer-header"><h4 class="bs-drawer-title" id="filter-drawer-title">Filters</h4><button class="bs-drawer-close" type="button" data-bs-dialog-dismiss aria-label="Close filters">&times;</button></header>
  <div class="bs-drawer-body"><fieldset class="bs-stack bs-gap-3"><legend class="bs-label">Status</legend><label class="bs-check"><input class="bs-check-input" type="checkbox" checked /><span class="bs-check-label">Active</span></label><label class="bs-check"><input class="bs-check-input" type="checkbox" /><span class="bs-check-label">Draft</span></label><label class="bs-check"><input class="bs-check-input" type="checkbox" /><span class="bs-check-label">Archived</span></label></fieldset></div>
</dialog>

Scroll to the final event without losing the title or the persistent footer action. The drawer itself remains exactly viewport height.

Recent activity

Events from the last seven days.

  1. Deployment completed

    Production · 14 minutes ago

  2. Review approved

    API changes · 38 minutes ago

  3. Member invited

    Design team · 1 hour ago

  4. Backup created

    Workspace · 2 hours ago

  5. Domain verified

    Settings · Yesterday

  6. Token rotated

    Security · Yesterday

  7. Release created

    Version 2.4.0 · Monday

  8. Audit exported

    Compliance · Monday

  9. Environment cloned

    Staging · Sunday

  10. Permission changed

    Members · Sunday

  11. Webhook delivered

    Integrations · Saturday

  12. Report generated

    Analytics · Saturday

  13. Branch protected

    Repository · Friday

  14. Key registered

    Security · Friday

  15. Billing contact updated

    Workspace · Thursday

  16. Export completed

    Data · Thursday

HTML · Scrollable drawer body
<button class="bs-btn bs-btn-secondary" type="button" data-bs-toggle="dialog" aria-controls="activity-drawer">View activity</button>
<dialog class="bs-drawer bs-drawer-end bs-drawer-lg" id="activity-drawer" data-bs-dialog aria-labelledby="activity-drawer-title">
  <header class="bs-drawer-header"><h4 class="bs-drawer-title" id="activity-drawer-title">Recent activity</h4><p class="bs-drawer-description">Events from the last seven days.</p><button class="bs-drawer-close" type="button" data-bs-dialog-dismiss aria-label="Close recent activity">&times;</button></header>
  <div class="bs-drawer-body"><ol class="bs-stack bs-gap-4"><li><strong>Deployment completed</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Production · 14 minutes ago</p></li><li><strong>Review approved</strong><p class="bs-text-muted bs-text-sm bs-mb-0">API changes · 38 minutes ago</p></li><li><strong>Member invited</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Design team · 1 hour ago</p></li><li><strong>Backup created</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Workspace · 2 hours ago</p></li><li><strong>Domain verified</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Settings · Yesterday</p></li><li><strong>Token rotated</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Security · Yesterday</p></li><li><strong>Release created</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Version 2.4.0 · Monday</p></li><li><strong>Audit exported</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Compliance · Monday</p></li><li><strong>Environment cloned</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Staging · Sunday</p></li><li><strong>Permission changed</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Members · Sunday</p></li><li><strong>Webhook delivered</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Integrations · Saturday</p></li><li><strong>Report generated</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Analytics · Saturday</p></li><li><strong>Branch protected</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Repository · Friday</p></li><li><strong>Key registered</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Security · Friday</p></li><li><strong>Billing contact updated</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Workspace · Thursday</p></li><li><strong>Export completed</strong><p class="bs-text-muted bs-text-sm bs-mb-0">Data · Thursday</p></li></ol></div>
  <footer class="bs-drawer-footer"><button class="bs-btn bs-btn-primary" type="button">Open activity log</button></footer>
</dialog>

Named and custom widths

Use compact through extra-large widths for common tasks, or set --bs-drawer-width directly when the panel must align with product content.

20rem drawer
36rem drawer
Custom 32rem drawer
HTML · Drawer widths
<div class="bs-flex bs-flex-wrap bs-gap-3">
  <button class="bs-btn bs-btn-secondary bs-btn-sm" type="button" data-bs-toggle="dialog" aria-controls="drawer-width-small">Small</button>
  <button class="bs-btn bs-btn-secondary bs-btn-sm" type="button" data-bs-toggle="dialog" aria-controls="drawer-width-large">Large</button>
  <button class="bs-btn bs-btn-secondary bs-btn-sm" type="button" data-bs-toggle="dialog" aria-controls="drawer-width-custom">Custom 32rem</button>
</div>
<dialog class="bs-drawer bs-drawer-end bs-drawer-sm" id="drawer-width-small" data-bs-dialog aria-label="Small drawer"><div class="bs-drawer-body"><strong>20rem drawer</strong><button class="bs-btn bs-btn-primary bs-mt-4" type="button" data-bs-dialog-dismiss>Done</button></div></dialog>
<dialog class="bs-drawer bs-drawer-end bs-drawer-lg" id="drawer-width-large" data-bs-dialog aria-label="Large drawer"><div class="bs-drawer-body"><strong>36rem drawer</strong><button class="bs-btn bs-btn-primary bs-mt-4" type="button" data-bs-dialog-dismiss>Done</button></div></dialog>
<dialog class="bs-drawer bs-drawer-end" id="drawer-width-custom" data-bs-dialog style="--bs-drawer-width: 32rem" aria-label="Custom width drawer"><div class="bs-drawer-body"><strong>Custom 32rem drawer</strong><button class="bs-btn bs-btn-primary bs-mt-4" type="button" data-bs-dialog-dismiss>Done</button></div></dialog>

Use the shared JavaScript controller

Drawer placement is CSS, so application code imports the same Dialog class and receives the same lifecycle events as a centered modal.

JavaScript · Drawer control
import { Dialog } from "@boobstrap/boobstrap/js/dialog";

const drawer = Dialog.getOrCreateInstance(
  document.querySelector("#account-drawer"),
);

drawer.show();
drawer.hide();

Control a drawer with Alpine

Use the bsDialog provider and drawer classes together. This stays compatible with Alpine's strict-CSP build.

HTML · Alpine.js drawer
<div x-data="bsDialog">
  <button class="bs-btn" type="button" x-bind="trigger" aria-controls="alpine-drawer">Open drawer</button>
  <dialog class="bs-drawer bs-drawer-end" id="alpine-drawer" x-ref="dialog" x-bind="panel" aria-labelledby="alpine-drawer-title">
    <header class="bs-drawer-header">
      <h2 class="bs-drawer-title" id="alpine-drawer-title">Account</h2>
      <button class="bs-drawer-close" type="button" x-bind="dismiss" aria-label="Close account drawer">&times;</button>
    </header>
    <div class="bs-drawer-body">...</div>
  </dialog>
</div>

Placement

Use exactly one logical placement class. Start and end automatically follow left-to-right and right-to-left document direction.

Width

Choose a named width by task complexity, not screen size alone. The framework caps every drawer safely on narrow viewports.

Scrolling

Keep the header and footer as direct regions and put long lists or forms in .bs-drawer-body, which owns vertical scrolling.

Dismissal

Backdrop dismissal is useful for exploratory panels. Disable it for edits that may contain unsaved work, and always retain Escape plus an explicit control.

Drawer class API

ClassPurpose
.bs-drawerCreates a full-height native dialog panel with a customizable width.
.bs-drawer-start / .bs-drawer-endPlaces the drawer on a logical edge and follows right-to-left document direction.
.bs-drawer-headerOptional fixed header grid for the title, description, and close control.
.bs-drawer-titleNames the drawer and connects through aria-labelledby.
.bs-drawer-descriptionOptional supporting copy connected through aria-describedby.
.bs-drawer-closeOptional icon-sized close control placed in the header.
.bs-drawer-bodyFlexible content region that independently scrolls to its final item.
.bs-drawer-footerOptional fixed, wrapping footer for persistent actions or status.
.bs-drawer-sm / .bs-drawer-lg / .bs-drawer-xlSelects 20rem, 36rem, or 48rem width; the unmodified drawer is 28rem.
--bs-drawer-widthOverrides the drawer width with any product-specific length.

Shared dialog behavior

Drawers use data-bs-dialog, data-bs-toggle="dialog", data-bs-dialog-dismiss, the optional backdrop setting, the Dialog JavaScript class, and the bsDialog Alpine provider. This keeps focus, Escape, scroll locking, events, and imperative control identical between presentations.