AdminKitDocs
Components

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.

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.

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

PropTypeDefaultDescription
showbooleanWhether the modal is open
onHide() => voidCalled on Escape and on a backdrop click; set show to false here
onExited() => voidCalled once the modal has faded out
backdropboolean | "static"true"static": a click outside shakes the dialog instead of closing it; false: no backdrop
keyboardbooleantrueEscape closes the modal
dialogClassNamestringClasses for .modal-dialog: modal-lg, modal-dialog-centered, ...
contentClassNamestringClasses for .modal-content
contentAsElementType"div""form" for a dialog that is a form
contentPropsobjectOther 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.

<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:

ClassEffect
modal-dialog-centeredCenters the dialog vertically
modal-dialog-scrollableScrolls the body instead of the page when the content is long
modal-sm, modal-lg, modal-xl400px, 900px and 1140px wide (600px by default)
modal-fullscreenCovers the whole window; modal-fullscreen-md-down only below a breakpoint
<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:

<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".

Last updated on

On this page