Bootstrap Drawer

Drawer

Bootstrap Drawer component built on the native <dialog> element — a floating, inset side panel with a blurred native backdrop, focus trapping, and responsive variants.

Overview

Added in v6.0.0

The Drawer component slides a floating, rounded panel in from any edge of the viewport — for navigation, shopping carts, filters, and settings. It renders on the browser’s native <dialog> element, so the top layer, ::backdrop, focus trapping, and dialog semantics come from the browser. It follows the design Bootstrap 6 introduces under the same name — an inset panel over a blurred, translucent backdrop.

Drawer shares its base behavior with Offcanvas. Offcanvas keeps the classic edge-anchored look and the v5 vocabulary; Drawer carries the new Bootstrap 6 look and the drawer-* classes. Pick one — they are independent components.

Example

Toggle a drawer by clicking the button below.

Drawer

The drawer content goes here. You can place nearly any Bootstrap component or custom element here.

<button type="button" class="btn-solid theme-primary" data-coreui-toggle="drawer" data-coreui-target="#exampleDrawer">
  Open drawer
</button>

<dialog class="drawer drawer-start" id="exampleDrawer" aria-labelledby="exampleDrawerLabel">
  <div class="drawer-header">
    <h5 class="drawer-title" id="exampleDrawerLabel">Drawer</h5>
    <button type="button" class="btn-close" data-coreui-dismiss="drawer" aria-label="Close"></button>
  </div>
  <div class="drawer-body">
    ...
  </div>
</dialog>

Placement

Drawers can slide in from any edge of the viewport — apply one of the placement classes:

  • .drawer-start — left side of the viewport
  • .drawer-end — right side of the viewport
  • .drawer-top — top of the viewport
  • .drawer-bottom — bottom of the viewport
  • .drawer-fullscreen — covers the viewport (minus the inset)
Right drawer

…

Top drawer

…

Bottom drawer

…

Fullscreen drawer

…

Variants

Translucent

Add .drawer-translucent for a frosted-glass panel that blurs the content behind it.

Translucent drawer

…

Sheet

Combine .drawer-bottom with .drawer-sheet for a flush-to-edge bottom sheet with no inset and squared bottom corners. On large viewports the sheet is centered with a maximum width (--cui-drawer-sheet-width).

Bottom sheet

…

Fit content

Add .drawer-fit-content to a .drawer-start/.drawer-end drawer to keep the panel only as tall as its content.

Add a .drawer-footer below the .drawer-body for the panel’s actions. It is a flex row that wraps and keeps its children spaced and vertically centered, so a row of buttons needs no utilities.

Backdrop presets

The backdrop reads --cui-backdrop-color, --cui-backdrop-opacity and --cui-backdrop-blur; a drawer defaults to a translucent tint of the page. For the dialog’s dark scrim add .backdrop-scrim; the presets work on every overlay component.

Dark backdrop
The scrim the dialog uses, on a drawer.
html
<button class="btn-solid theme-primary" type="button" data-coreui-toggle="drawer" data-coreui-target="#drawerScrim">
  Toggle drawer with a dark backdrop
</button>

<dialog class="drawer drawer-start backdrop-scrim" id="drawerScrim" aria-labelledby="drawerScrimLabel">
  <div class="drawer-header">
    <h5 class="drawer-title" id="drawerScrimLabel">Dark backdrop</h5>
    <button type="button" class="btn-close" data-coreui-dismiss="drawer" aria-label="Close"></button>
  </div>
  <div class="drawer-body">
    The scrim the dialog uses, on a drawer.
  </div>
</dialog>

No backdrop

Add .drawer-no-backdrop to hide the backdrop while keeping the drawer modal — the panel still takes the top layer and traps focus, but nothing dims the page behind it.

Instant

Add .drawer-instant to a drawer that should appear and disappear at once, with no slide and no backdrop fade. It is the same opt-out that .alert-instant provides, and it applies to both directions.

Dark

Put data-coreui-theme="dark" on the drawer and it flips the tokens its own surface reads, so the panel and everything in it — including the close button — turns dark on a light page.

Dark drawer

The surface, the text and the dividers all read the flipped tokens.

<dialog class="drawer drawer-start" data-coreui-theme="dark">
  ...
</dialog>

Body scrolling

The page scroll is blocked while a drawer is open. Set scroll: true to open the drawer non-modally instead — the page stays interactive and scrollable. A visible backdrop requires the top layer, so backdrop: true (the default) takes precedence over scroll: true for the page-interactivity part.

Drawer with body scrolling

Scroll further down the page to see this option in action.

html
<button class="btn-solid theme-primary" type="button" data-coreui-toggle="drawer" data-coreui-target="#drawerScrolling">Enable body scrolling</button>

<dialog class="drawer drawer-start" data-coreui-scroll="true" data-coreui-backdrop="false" id="drawerScrolling" aria-labelledby="drawerScrollingLabel">
  <div class="drawer-header">
    <h5 class="drawer-title" id="drawerScrollingLabel">Drawer with body scrolling</h5>
    <button type="button" class="btn-close" data-coreui-dismiss="drawer" aria-label="Close"></button>
  </div>
  <div class="drawer-body">
    <p>Scroll further down the page to see this option in action.</p>
  </div>
</dialog>

Static backdrop

When the backdrop is set to static, the drawer will remain open when clicking outside of it.

Drawer
I won’t close if you click outside of me.
html
<button class="btn-solid theme-primary" type="button" data-coreui-toggle="drawer" data-coreui-target="#drawerStatic">
  Toggle static drawer
</button>

<dialog class="drawer drawer-start" data-coreui-backdrop="static" id="drawerStatic" aria-labelledby="drawerStaticLabel">
  <div class="drawer-header">
    <h5 class="drawer-title" id="drawerStaticLabel">Drawer</h5>
    <button type="button" class="btn-close" data-coreui-dismiss="drawer" aria-label="Close"></button>
  </div>
  <div class="drawer-body">
    <div>
      I won’t close if you click outside of me.
    </div>
  </div>
</dialog>

Responsive

Responsive drawer classes hide content in a drawer below a specified breakpoint and render it inline above it. For example, .drawer-lg acts as a drawer below the lg breakpoint and displays the content inline above it.

Resize your browser to show the responsive drawer toggle.
Responsive drawer

This is content within a .drawer-lg.

html
<button class="btn-solid theme-primary d-lg-none" type="button" data-coreui-toggle="drawer" data-coreui-target="#drawerResponsive" aria-controls="drawerResponsive">Toggle drawer</button>

<div class="alert theme-info d-none d-lg-block">Resize your browser to show the responsive drawer toggle.</div>

<dialog class="drawer-lg drawer-end" id="drawerResponsive" aria-labelledby="drawerResponsiveLabel">
  <div class="drawer-header">
    <h5 class="drawer-title" id="drawerResponsiveLabel">Responsive drawer</h5>
    <button type="button" class="btn-close" data-coreui-dismiss="drawer" data-coreui-target="#drawerResponsive" aria-label="Close"></button>
  </div>
  <div class="drawer-body">
    <p class="mb-0">This is content within a <code>.drawer-lg</code>.</p>
  </div>
</dialog>

Responsive drawer classes are available for each breakpoint.

  • .drawer
  • .drawer-sm
  • .drawer-md
  • .drawer-lg
  • .drawer-xl
  • .drawer-2xl

Accessibility

Since the drawer is conceptually a modal dialog, be sure to add aria-labelledby="..." — referencing the drawer title — to .drawer. The native <dialog> element already provides the dialog role and focus management — don’t add role, aria-modal, aria-hidden, or tabindex yourself.

Usage

Via data attributes

Include data-coreui-toggle="drawer" along with a data-coreui-target or href in the element to automatically control a drawer. Dismiss from within with data-coreui-dismiss="drawer". To have a drawer open by default, add the open attribute.

Via JavaScript

const drawerElementList = document.querySelectorAll('.drawer')
const drawerList = [...drawerElementList].map(drawerEl => new coreui.Drawer(drawerEl))

Options

Options can be passed using data attributes or JavaScript. To do this, append an option name to data-coreui-, such as data-coreui-backdrop="static".

PropertyTypeDefault
backdropboolean or the string statictrue

Shows the native ::backdrop while the drawer is open. Alternatively, specify static for a backdrop that doesn’t close the drawer when clicked, or false to visually suppress it.

keyboardbooleantrue

Closes the drawer when the escape key is pressed.

scrollbooleanfalse

Allows body scrolling while the drawer is open — the drawer then opens non-modally, keeping the rest of the page interactive.

Methods

Promises and transitions

Methods that show or hide a component return a promise. The promise resolves when the transition ends, so await continues at the same moment a shown.coreui.* or hidden.coreui.* listener runs. The methods still return immediately and never block the caller. A call on a component that is already transitioning does nothing and resolves right away.

Refer to our JavaScript documentation for further details.

MethodDescription
disposeDestroys an element’s drawer. (Removes stored data on the DOM element)
getInstanceStatic method that allows you to obtain the drawer instance associated with a DOM element
getOrCreateInstanceStatic method that enables you to retrieve the drawer instance linked to a DOM element, or create a new one if it hasn’t been initialized
hideHides a drawer. Returns a promise that resolves after the drawer is hidden (i.e. after the hidden.coreui.drawer event occurs).
showDisplays a drawer. Returns a promise that resolves after the drawer is shown (i.e. after the shown.coreui.drawer event occurs).
toggleSwitches a drawer between shown and hidden. Returns a promise that resolves after the drawer is shown or hidden (i.e. after the shown.coreui.drawer or hidden.coreui.drawer event occurs).

Events

Event typeDescription
hidden.coreui.drawerThis event is fired when a drawer is hidden from the user (it will wait for CSS transitions to complete).
hide.coreui.drawerThis event is fired immediately after the hide method has been called.
hidePrevented.coreui.drawerThis event is triggered when the drawer is shown, its backdrop is static, and a click outside of the drawer occurs. The event is also triggered when the escape key is pressed and the keyboard option is set to false.
show.coreui.drawerThis event fires immediately when the show instance method is invoked.
shown.coreui.drawerThis event is fired when a drawer becomes visible to the user (it will wait for CSS transitions to complete).
const myDrawer = document.getElementById('myDrawer')
myDrawer.addEventListener('hidden.coreui.drawer', event => {
  // do something...
})

Customizing

CSS variables

Drawers use local CSS variables on .drawer for enhanced real-time customization.

scss
$drawer-tokens: defaults(
  (
    --cui-drawer-inset: var(--cui-spacer),
    --cui-drawer-zindex: var(--cui-z-drawer),
    --cui-drawer-width: 400px,
    --cui-drawer-height: 30vh,
    --cui-drawer-sheet-width: 760px,
    --cui-drawer-padding-x: var(--cui-spacer),
    --cui-drawer-padding-y: var(--cui-spacer),
    --cui-drawer-color: var(--cui-fg-body),
    --cui-drawer-bg: var(--cui-bg-body),
    --cui-drawer-border-width: var(--cui-border-width),
    --cui-drawer-border-color: var(--cui-border-color-translucent),
    --cui-drawer-border-radius: var(--cui-radius-7),
    --cui-drawer-box-shadow: var(--cui-box-shadow-lg),
    --cui-drawer-transition-property: #{"transform, visibility"},
    --cui-drawer-transition-duration: .3s,
    --cui-drawer-transition-timing: var(--cui-transition-timing-overlay),
    --cui-drawer-title-line-height: var(--cui-body-line-height),
    --cui-drawer-body-gap: var(--cui-drawer-padding-y),
    --cui-drawer-footer-gap: .5rem,
    --cui-backdrop-color: var(--cui-bg-body),
    --cui-backdrop-opacity: 25%,
    --cui-drawer-backdrop-bg: color-mix(in oklch, var(--cui-backdrop-color) var(--cui-backdrop-opacity), transparent),
    --cui-drawer-backdrop-blur: var(--cui-backdrop-blur),
    --cui-drawer-scale-transform: scale(1.02),
  ),
  $drawer-tokens
);