# React Bootstrap Modal

A React Bootstrap modal component: open the dialog from state, close it on Escape or a backdrop click. Static, centered, scrollable and fullscreen variants.

Use a modal when you need the user's attention before they go on: to confirm a delete, fill in a short form or read an important message. The page behind it dims and stays out of reach until the modal closes.

`Modal` is a React component, so you open it with state instead of Bootstrap's `data-bs-toggle` attributes. It looks and animates like Bootstrap's modal, without Bootstrap's JavaScript or react-bootstrap. It is in the free and PRO versions of [AdminKit for React](/react/).

## Example

Keep a `show` state in the component that has the button. `show` opens the modal, and `onHide` is called when the user presses Escape or clicks next to the dialog. Inside `Modal` you write the header, body and footer as in Bootstrap.

```html
<div class="modal" tabindex="-1" style="display: block; position: relative; z-index: 0;" aria-labelledby="modalExampleTitle">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title h5" id="modalExampleTitle">Delete project</h2>
        <button type="button" class="btn-close" aria-label="Close"></button>
      </div>
      <div class="modal-body">This cannot be undone.</div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary">Cancel</button>
        <button type="button" class="btn btn-danger">Delete</button>
      </div>
    </div>
  </div>
</div>
```
```tsx code
import { useState } from "react";
import { CloseButton } from "@/components/bootstrap/close-button";
import { Modal } from "@/components/bootstrap/modal";

export function DeleteProject() {
  const [show, setShow] = useState(false);
  const close = () => setShow(false);

  return (
    <>
      <button type="button" className="btn btn-danger" onClick={() => setShow(true)}>
        Delete project
      </button>
      <Modal show={show} onHide={close} aria-labelledby="delete-title">
        <div className="modal-header">
          <h2 className="modal-title h5" id="delete-title">Delete project</h2>
          <CloseButton onClick={close} />
        </div>
        <div className="modal-body">This cannot be undone.</div>
        <div className="modal-footer">
          <button type="button" className="btn btn-secondary" onClick={close}>Cancel</button>
          <button type="button" className="btn btn-danger" onClick={close}>Delete</button>
        </div>
      </Modal>
    </>
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `show` | `boolean` | | Whether the modal is open |
| `onHide` | `() => void` | | Called on Escape and on a backdrop click; set `show` to false here |
| `onExited` | `() => void` | | Called once the modal has faded out |
| `backdrop` | `boolean \| "static"` | `true` | `"static"`: a click outside shakes the dialog instead of closing it; `false`: no backdrop |
| `keyboard` | `boolean` | `true` | Escape closes the modal |
| `dialogClassName` | `string` | | Classes for `.modal-dialog`: `modal-lg`, `modal-dialog-centered`, ... |
| `contentClassName` | `string` | | Classes for `.modal-content` |
| `contentAs` | `ElementType` | `"div"` | `"form"` for a dialog that is a form |
| `contentProps` | `object` | | Other props for `.modal-content`, such as `onSubmit` |

Other props, such as `className`, `id` and `aria-labelledby`, go to the `.modal` element. The component is in `src/components/bootstrap/modal.tsx`, if you want to read or change it.

## Static backdrop

Sometimes the user has to make a choice before the modal can close. With `backdrop="static"` and `keyboard={false}`, a click next to the dialog or Escape makes it bounce instead of closing.

```html
<div class="modal fade" id="modalStatic" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="modalStaticTitle" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title h5" id="modalStaticTitle">Unsaved changes</h2>
      </div>
      <div class="modal-body">Save your changes before you leave this page?</div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Discard</button>
        <button type="button" class="btn btn-primary" data-bs-dismiss="modal">Save</button>
      </div>
    </div>
  </div>
</div>

<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalStatic">
  Leave page
</button>
```
```tsx code
<Modal
  show={show}
  onHide={close}
  backdrop="static"
  keyboard={false}
  aria-labelledby="unsaved-title"
>
  <div className="modal-header">
    <h2 className="modal-title h5" id="unsaved-title">Unsaved changes</h2>
  </div>
  <div className="modal-body">Save your changes before you leave this page?</div>
  <div className="modal-footer">
    <button type="button" className="btn btn-secondary" onClick={discard}>Discard</button>
    <button type="button" className="btn btn-primary" onClick={save}>Save</button>
  </div>
</Modal>
```

## Centered, scrollable and sizes

Bootstrap's `.modal-dialog` classes go in `dialogClassName`:

| Class | Effect |
| --- | --- |
| `modal-dialog-centered` | Centers the dialog vertically |
| `modal-dialog-scrollable` | Scrolls the body instead of the page when the content is long |
| `modal-sm`, `modal-lg`, `modal-xl` | 400px, 900px and 1140px wide (600px by default) |
| `modal-fullscreen` | Covers the whole window; `modal-fullscreen-md-down` only below a breakpoint |

```html
<div class="modal fade" id="modalCentered" tabindex="-1" aria-labelledby="modalCenteredTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title h5" id="modalCenteredTitle">Invite members</h2>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">A large, vertically centered dialog.</div>
    </div>
  </div>
</div>

<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalCentered">
  Invite members
</button>
```
```tsx code
<Modal
  show={show}
  onHide={close}
  dialogClassName="modal-dialog-centered modal-lg"
  aria-labelledby="invite-title"
>
  <div className="modal-header">
    <h2 className="modal-title h5" id="invite-title">Invite members</h2>
    <CloseButton onClick={close} />
  </div>
  <div className="modal-body">A large, vertically centered dialog.</div>
</Modal>
```

For a colored modal, as in the demo, give the modal `className="modal-colored modal-primary"` (or another theme color).

## Form in a modal

To put a form in a modal, make the modal itself the form with `contentAs="form"`. The submit button in the footer then submits it, and so does Enter in a field:

```tsx
<Modal
  show={show}
  onHide={close}
  contentAs="form"
  contentProps={{
    onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      rename(new FormData(event.currentTarget).get("name") as string);
      close();
    },
  }}
  aria-labelledby="rename-title"
>
  <div className="modal-header">
    <h2 className="modal-title h5" id="rename-title">Rename project</h2>
    <CloseButton onClick={close} />
  </div>
  <div className="modal-body">
    <label htmlFor="project-name" className="form-label">Name</label>
    <input id="project-name" name="name" className="form-control" required />
  </div>
  <div className="modal-footer">
    <button type="submit" className="btn btn-primary">Save</button>
  </div>
</Modal>
```

## Accessibility

- The modal has `role="dialog"` and `aria-modal="true"`. Give it a title and point `aria-labelledby` at the title's `id`.
- When it opens, the focus moves into the modal, and Tab and Shift+Tab stay inside it. When it closes, the focus goes back to the element that had it, usually the button that opened it.
- Escape closes the modal, unless `keyboard={false}`. Then keep a visible button that closes it.
- `CloseButton` has `aria-label="Close"`.

## Related

- [Next.js Bootstrap modal](/nextjs/docs/components/modal/): the same component in AdminKit for Next.js
- [Bootstrap 5 modal](/bootstrap/docs/components/modal/): the modal of the HTML version
- [Offcanvas](/react/docs/components/offcanvas/): a panel that slides in from the side
- [Modals demo](https://react-demo.adminkit.io/ui-modals): default, colored, centered and sized modals

---

Canonical HTML page: https://adminkit.io/react/docs/components/modal/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
