Skip to main content

Drawer

Build hidden sidebars or drawers into your project for navigation, shopping carts, and more with a few classes. Drawer replaces the old Offcanvas component.

Idea React provides the Drawer wrapper, which composes the native <dialog> element, a header with a close button, a body, and an optional footer. The examples below are interactive and adapt the upstream Drawer documentation.

How it works

Drawer builds on the native <dialog> element and the NativeDialog wrapper to manage hidden sidebars. These sidebars, or drawers, can appear from the left, right, top, or bottom edge of the viewport.

  • Drawer shares the same base as the Dialog component, using native <dialog> APIs for focus trapping, backdrop, and top-layer rendering.
  • Use the <dialog> element, not a <div>. Due to how CSS handles animations, don't apply margin or translate directly to .drawer—wrap it if you need those.
  • Modal by default—opens with showModal(), a backdrop, and focus trapping. Pass modal={false} and backdrop={false} to open as a non-modal drawer with show(): body scrolling stays on, and focus is not trapped.
  • Built-in backdrop via ::backdrop (modal only) closes the drawer on click; set backdrop="static" to lock clicks outside, or backdrop={false} to hide it.
  • Escape key handling closes the drawer by default; set keyboard={false} to disable.
  • Only one drawer can be shown at a time, because a modal native dialog holds the top layer.
  • Animated open and close—drawer slides back out to its placement-specific off-screen position on close, with the ::backdrop fading alongside it when opened modally. Add .drawer-instant to skip animations.

Examples

Drawer components

Below is a drawer with a header and a close button. We suggest that you include drawer headers with dismiss actions whenever possible, or provide an explicit dismiss action.

Drawer

Content for the drawer goes here. You can place just about any Bootstrap component or custom elements here.

Placement

There's no default placement for drawer components, so the wrapper defaults to end and accepts start, end, top, or bottom. Try the placements below.

Drawer start

drawer-start places the drawer on the left of the viewport.

Drawer end

drawer-end places the drawer on the right of the viewport.

Drawer top

drawer-top places the drawer on the top of the viewport.

Drawer bottom

drawer-bottom places the drawer on the bottom of the viewport.

  • drawer-start places drawer on the left of the viewport
  • drawer-end places the drawer on the right of the viewport
  • drawer-top places the drawer on the top of the viewport
  • drawer-bottom places the drawer on the bottom of the viewport

Footer

Add an optional footer for action buttons or other content at the bottom of the drawer.

Drawer with footer

Content for the drawer goes here. The footer will stick to the bottom.

Without JavaScript

Supported browsers can open and close a drawer with the Invoker Commands API—no JavaScript required. Set commandfor to the drawer's id, use command="show-modal" to open, and command="close" to dismiss.

The Invoker Commands API is not supported in all browsers. Pass open and onDismiss to control the drawer through React state as in the examples above when you need consistent behavior or an animated close.

<button type="button" className="btn-solid theme-primary" commandfor="invokerDrawer" command="show-modal">
Open drawer
</button>

<dialog className="drawer drawer-start" id="invokerDrawer" aria-labelledby="invokerDrawerLabel">
<DrawerHeader>
<DrawerTitle id="invokerDrawerLabel">Invoker Commands</DrawerTitle>
<button
type="button"
className="btn-close"
commandfor="invokerDrawer"
command="close"
aria-label="Close"
/>
</DrawerHeader>
<DrawerBody>
<p>This drawer opens with `command="show-modal"` and closes with `command="close"`.</p>
</DrawerBody>
</dialog>

Body scrolling

Scrolling the <body> element is disabled when a drawer opens modally. Use modal={false} with backdrop={false} to keep body scrolling and open the drawer as non-modal. Focus moves into the drawer, but is not trapped, so the page stays interactive.

Drawer with body scrolling

Try scrolling the rest of the page to see this option in action.

Body scrolling & backdrop

You can also allow <body> scrolling while keeping a backdrop. With a backdrop present, the drawer stays modal (showModal() and focus trapping) even when modal={false}.

Backdrop with scrolling

Try scrolling the rest of the page to see this option in action.

Static backdrop

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

Drawer

I will not close if you click outside of me.

Instant drawer

By default, drawers animate with a slide on open and close. To disable animations, add .drawer-instant to the drawer element.

Instant drawer

This drawer doesn't animate and appears instantly on open and close.

Dark drawer

Change the appearance of drawers with utilities to better match them to different contexts like dark navbars. Set theme="dark", which applies data-bs-theme="dark" to the drawer, and all nested elements within use dark mode.

Drawer

Place drawer content here.

Translucent drawer

Add .drawer-translucent to the drawer panel to blur and saturate the background. This makes the panel semi-transparent so content behind it shows through.

Translucent drawer

Content for the translucent drawer goes here. The background blurs and saturates whatever is behind it.

Sheet

Add .drawer-sheet to render the panel flush against the viewport edge. The sheet variant removes the drawer's inset, border, border-radius, and box-shadow, so it sits edge-to-edge like a traditional offcanvas. It works with any placement.

Sheet drawer

Content for the sheet drawer goes here. It sits flush to the edge with no inset, border, rounded corners, or shadow.

Accessibility

Since the drawer is a native <dialog>, it gets the correct dialog role. Modal drawers also get modal semantics and focus trapping from showModal(). Non-modal drawers (modal={false} and backdrop={false}) use show() instead, so they do not trap focus or mark the rest of the page as inert. The wrapper sets aria-label from a string title; pass aria-label or aria-labelledby yourself when the title is not a string.

Usage

The Drawer wrapper composes the classes and attributes that the upstream plugin handles:

  • .drawer and .drawer-{placement} on a <dialog> element enable drawer behavior.
  • DrawerHeader, DrawerTitle, DrawerBody, and DrawerFooter are exported to compose custom drawer markup.
  • The header contains Drawer's title and a CloseButton that calls onDismiss.
  • footer renders a .drawer-footer; bodyClassName extends the .drawer-body.
  • Additional classes such as drawer-instant, drawer-translucent, and drawer-sheet pass through className.

Set open and onDismiss to control the drawer through React state. The native dialog keeps onClose in sync when the user closes the drawer with Escape or a backdrop click.

NameTypeDefaultDescription
titleReactNodeHeader content; a string also becomes the drawer's aria-label.
placementstart | end | top | bottomendEdge of the viewport the drawer slides in from.
footerReactNodeContent rendered in .drawer-footer.
bodyClassNamestringExtra classes for .drawer-body.
onDismiss() => voidCalled when the close button, Escape, or a backdrop click closes the drawer.
themelight | darkSets data-bs-theme on the dialog.
modalbooleantrueOpens with showModal() (focus trap and top layer) or show().
backdropboolean | statictrueShows a backdrop; static prevents closing when clicking outside.
keyboardbooleantrueCloses the drawer when Escape is pressed.

CSS

Drawer styling uses Bootstrap's .drawer CSS variables for width, height, padding, background, border, radius, shadow, and animation. Backdrop tokens style the native ::backdrop pseudo-element. See the upstream drawer CSS for the variable and Sass reference.