Skip to main content

Dialog

A modern component built on the native <dialog> element with built-in accessibility and backdrop support that replaces the old Modal component.

Idea React provides the NativeDialog wrapper, which applies Bootstrap's dialog classes and manages native showModal() / show() calls, Escape handling, backdrop clicks, and focus return. The same module exports DialogHeader, DialogTitle, DialogBody, and DialogFooter for the dialog structure. The examples below are interactive and adapt the upstream Dialog documentation.

How it works

The Dialog component leverages the browser's native <dialog> element, providing built-in accessibility features, focus management, and backdrop handling without the complexity of custom implementations.

Key features of the native dialog:

  • Modal or inline via showModal() / show() — modal: true (default) promotes the dialog to the browser's top layer with a backdrop and focus trapping; modal: false renders it inline.
  • Built-in backdrop using the ::backdrop pseudo-element (modal only); set backdrop: "static" to lock clicks outside, or backdrop: false to hide it.
  • Escape key handling closes the dialog by default; set keyboard: false to disable.
  • Accessibility — focus is trapped inside modal dialogs and returned to the trigger on close, with native <dialog> ARIA semantics.
  • Animated open and close — circumvent browser restrictions by using a .hiding class to keep dialogs in the top layer during close so the exit transition (including ::backdrop) are animated properly.

Animations respect the prefers-reduced-motion setting.

Example

Toggle a dialog by clicking the button below. The dialog uses the native showModal() API for true modal behavior.

Dialog title

This is a native dialog element. It uses the browser's built-in modal behavior for accessibility and focus management.

The open prop controls the browser's native dialog state, and onClose keeps React in sync when the user closes the dialog with Escape or a backdrop click.

Without JavaScript

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

The Invoker Commands API is not supported in all browsers. For guaranteed support, control the dialog through React state as in the examples above when you need plugin-like options, events, or an animated close.

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

<NativeDialog id="invokerDialog" aria-label="Invoker Commands">
<DialogHeader>
<DialogTitle>Invoker Commands</DialogTitle>
<button type="button" className="btn-close" commandfor="invokerDialog" command="close" aria-label="Close" />
</DialogHeader>
<DialogBody>
<p>
This dialog opens with <code>command="show-modal"</code> and closes with{' '}
<code>command="close"</code>. No Bootstrap JavaScript is involved.
</p>
</DialogBody>
<DialogFooter>
<button type="button" className="btn-solid theme-secondary" commandfor="invokerDialog" command="close">
Close
</button>
</DialogFooter>
</NativeDialog>

Dark dialog

To make a dialog dark, pass theme="dark", which applies data-bs-theme="dark" to the dialog element.

Dark dialog title

This is a native dialog element, only it's set to dark mode. It uses the browser's built-in modal behavior for accessibility and focus management.

Static backdrop

When backdrop is set to static, the dialog will not close when clicking outside of it. Click the button below to try it.

Static backdrop

I will not close if you click outside of me. Use the close button or press Escape.

Overlays

Tooltips, popovers, and toasts all work inside dialogs, but require some extra care. Since dialogs render in the browser's top layer, overlays appended to <body> will appear behind the dialog. The React components below render their overlays inside the dialog element, so tooltips and popovers stay in the top layer. Toasts already work as long as they're placed in the dialog markup.

Tooltips, popovers, toasts, and menus all work inside modal dialogs. Modal dialogs use showModal(), which promotes the dialog to the browser's top layer—a special rendering layer above everything else on the page.

Overlays inside dialogs

These components work inside modal dialogs.

Animations

Instant

By default, dialogs animate with a fade on open and close. To disable animations, add .dialog-instant to the <dialog> element.

Instant dialog

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

Slide down

Add .dialog-slide-down to the <dialog> and it will slide down from the top of the viewport when opened.

Slide down dialog

This dialog slides down from the top of the viewport.

Slide up

Add .dialog-slide-up to the <dialog> and it will slide up from the bottom of the viewport when opened.

Slide up dialog

This dialog slides up from the bottom of the viewport.

Scrollable

Create a scrollable dialog that scrolls the dialog body while keeping the header and footer fixed. Add .dialog-scrollable to the .dialog element.

Scrollable body

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This is some placeholder content to show the scrolling behavior for dialogs. We use repeated line breaks to demonstrate how content can exceed the dialog's inner height, showing scrolling within the body while the header and footer remain fixed.

This content should appear at the bottom after you scroll.

Swapping dialogs

When a toggle trigger is inside an open dialog, clicking it will swap dialogs—opening the new one before closing the current. This ensures the backdrop stays visible throughout the transition with no flash. In React, render each dialog with the same open state and switch which dialog is open.

First dialog

Click below to swap to a second dialog. The React state change opens the next dialog while the current one closes.

Second dialog

This is the second dialog. You can swap back to the first, or close this one entirely.

Non-modal dialogs

By default, dialogs open as modals using the browser-native showModal() method. You can also open dialogs as non-modal using show() by setting modal to false. Non-modal dialogs:

  • Have no backdrop
  • Don't trap focus
  • Don't block interaction with the rest of the page
  • Don't render in the browser's top layer
  • Still respond to Escape key (if keyboard: true)

Non-modal dialog

This dialog doesn't block the page. You can still interact with content behind it.

Use the modal prop to create a non-modal dialog when building with React:

<NativeDialog modal={false} open={open} onClose={close}>
…
</NativeDialog>

Optional sizes

Dialogs have three optional sizes, available via modifier classes to be placed on a .dialog. These sizes kick in at certain breakpoints to avoid horizontal scrollbars on narrower viewports.

SizeClassDialog max-width
Small.dialog-sm280px
Default—500px
Large.dialog-lg800px
Extra large.dialog-xl1140px

Extra large dialog

This is an extra large dialog using the .dialog-xl class.

Large dialog

This is a large dialog using the .dialog-lg class.

Medium (default) dialog

This is a medium (default) dialog—there's no modifier class for this size.

Small dialog

This is a small dialog using the .dialog-sm class.

Fullscreen dialog

Use .dialog-fullscreen to make the dialog cover the entire viewport.

Fullscreen dialog

This dialog covers the entire viewport.

Responsive fullscreen variants are also available. These make the dialog fullscreen only below a specific breakpoint.

ClassFullscreen below
.dialog-fullscreenAlways
.sm-down:dialog-fullscreen576px
.md-down:dialog-fullscreen768px
.lg-down:dialog-fullscreen1024px
.xl-down:dialog-fullscreen1280px
.2xl-down:dialog-fullscreen1536px

Fullscreen below lg

This dialog is fullscreen below the lg breakpoint.

CSS

Dialogs use CSS variables for real-time customization. Dialog sizes are defined in the $dialog-sizes Sass map, and each size specifies the dialog width. See the upstream dialog CSS for the complete variable and Sass reference.

React behavior

Bootstrap's dialog plugin data attributes map to props on the NativeDialog wrapper:

PropTypeDefaultDescription
openbooleanfalseControls the native dialog state. Updates call showModal() / show() and close().
modalbooleantrueWhen true, opens with showModal() for backdrop, focus trapping, and top-layer rendering. When false, opens with show() without a backdrop or focus trapping.
backdropboolean | 'static'trueFor modal dialogs, clicking the backdrop dismisses the dialog. Specify static for a backdrop that doesn't close the dialog when clicked.
keyboardbooleantrueCloses the dialog when the Escape key is pressed.
theme'light' | 'dark'—Applies a Bootstrap color mode to the dialog.

Dismissal does not require data-bs-dismiss: render a button inside the dialog and set the open state to false in its click handler. Use CloseButton for the standard close icon.

Native close and cancel events are forwarded as the onClose and onCancel callbacks. For imperative control, keep a ref to NativeDialog and call its show() and close() methods; the controlled open prop is preferred for React state synchronization.

The Dialog model builds on the wrapper with promise-based dialogs: open() returns a promise that resolves or rejects when the dialog closes.

Promise-based dialogs

Create the model with a render function, then await open() to collect the result. The example below resolves with the submitted numbers and rejects when the dialog is dismissed.

Dialog

const inputDialog = new Dialog(({ defer }) => (
<NativeDialog open={!!defer} onClose={() => defer?.reject(new DialogClose())}>
<form onSubmit={submit}>
<input className="form-control" name="a" type="number" />
</form>
</NativeDialog>
));

const data = await inputDialog.open();