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: falserenders it inline. - Built-in backdrop using the
::backdroppseudo-element (modal only); setbackdrop: "static"to lock clicks outside, orbackdrop: falseto hide it. - Escape key handling closes the dialog by default; set
keyboard: falseto 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
.hidingclass 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.
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.
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.
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.
Animations
Instant
By default, dialogs animate with a fade on open and close. To disable animations, add
.dialog-instant to the <dialog> element.
Slide down
Add .dialog-slide-down to the <dialog> and it will slide down from the top of the viewport
when opened.
Slide up
Add .dialog-slide-up to the <dialog> and it will slide up from the bottom of the viewport
when opened.
Scrollable
Create a scrollable dialog that scrolls the dialog body while keeping the header and footer fixed.
Add .dialog-scrollable to the .dialog element.
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.
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)
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.
| Size | Class | Dialog max-width |
|---|---|---|
| Small | .dialog-sm | 280px |
| Default | — | 500px |
| Large | .dialog-lg | 800px |
| Extra large | .dialog-xl | 1140px |
Fullscreen dialog
Use .dialog-fullscreen to make the dialog cover the entire viewport.
Responsive fullscreen variants are also available. These make the dialog fullscreen only below a specific breakpoint.
| Class | Fullscreen below |
|---|---|
.dialog-fullscreen | Always |
.sm-down:dialog-fullscreen | 576px |
.md-down:dialog-fullscreen | 768px |
.lg-down:dialog-fullscreen | 1024px |
.xl-down:dialog-fullscreen | 1280px |
.2xl-down:dialog-fullscreen | 1536px |
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:
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls the native dialog state. Updates call showModal() / show() and close(). |
modal | boolean | true | When true, opens with showModal() for backdrop, focus trapping, and top-layer rendering. When false, opens with show() without a backdrop or focus trapping. |
backdrop | boolean | 'static' | true | For modal dialogs, clicking the backdrop dismisses the dialog. Specify static for a backdrop that doesn't close the dialog when clicked. |
keyboard | boolean | true | Closes 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.
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();