Black Lives Matter. Support the Equal Justice Initiative.
Skip to main contentCarbon Design System

Modal

How to build a Modal using React. For code usage with other frameworks, please follow the links in the live demo on the usage tab.

Overview

There are two choices for you to use Carbon modals.

Modal typePurpose
<Modal>For putting simple contents in
<ComposedModal>For more flexibility in the contents

<Modal> allows you to use its bespoke set of contents. children prop maps to the modal body content. You can also use modalLabel, modalHeading, secondaryButtonText and primaryButtonText props to change the corresponding text.

Modifiers
size

ComposedModal

<ComposedModal> provides you with more flexibility in the contents. You can provide the contents via <ModalHeader>, <ModalBody> and <ModalFooter> with your contents in them.

ComposedModal
size
ModalFooter
Modifiers

Opening/closing modal

For both modal variants, you can open/close the modal by changing the open prop. For example, you can implement a launcher button with a React state and a <Button> that changes the state:

function ModalStateManager() {
const [open, setOpen] = useState(false);
return (
<>
{typeof document === "undefined"
? null
: ReactDOM.createPortal(
<ComposedModal open={open} onClose={() => setOpen(false)}>
...

You can create an abstract version of such state manager, as shown below.

const ModalStateManager = ({
renderLauncher: LauncherContent,
children: ModalContent,
}) => {
const [open, setOpen] = useState(false);
return (
<>
{!ModalContent || typeof document === "undefined"
? null

There are four responsive modal sizes: xs, small, default, and large. You can set it via size prop.

<ComposedModal size="lg">
<ModalHeader />
<ModalBody>
<p className="bx--modal-content__text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
cursus fermentum risus, sit amet fringilla nunc pellentesque quis. Duis
quis odio ultrices, cursus lacus ac, posuere felis. Donec dignissim libero
in augue mattis, a molestie metus vestibulum. Aliquam placerat felis
ultrices lorem condimentum, nec ullamcorper felis porttitor.

Alignment

Depending on the modal size of your choice and the viewport size, <Modal>/<ComposedModal> adds 20% padding at the right of the modal body content. Carbon design specifies that such 20% padding shouldn’t be applied to form elements. You can set hasForm prop to <Modal>/<ModalBody> to remove the padding, and use bx--modal-content__regular-content class to apply the 20% padding to non-form contents, as shown below.

<ComposedModal>
<ModalHeader />
<ModalBody hasForm>
<TextInput data-modal-primary-focus labelText="Enter something" />
<p className="bx--modal-content__text bx--modal-content__regular-content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
cursus fermentum risus, sit amet fringilla nunc pellentesque quis. Duis
quis odio ultrices, cursus lacus ac, posuere felis. Donec dignissim libero
in augue mattis, a molestie metus vestibulum. Aliquam placerat felis

Overflow content

In cases where the largest modal size cannot fit your modal body content in, Carbon design specifies to have a “visual fade” look at the end of the modal body area to indicate there is additional content out of view. You can set hasScrollingContent prop to <Modal>/<ModalBody> to do that, as shown below.

<ComposedModal size="large">
<ModalHeader />
<ModalBody hasScrollingContent>
<p className=".bx--modal-content__text">Some very large contents...</p>
</ModalBody>
</ComposedModal>

With <Modal>, you have limited control over the set of buttons. The following table shows the supported patterns.

Button group variantUsage
No button (passive modal)Use passiveModal prop in <Modal>
One buttonUse <ComposedModal>
Two buttonsUse <Modal> without passiveModal prop
Two buttons with danger buttonUse danger prop in <Modal>
<Modal danger>
<ModalHeader />
<p className="bx--modal-content__text">The modal body content</p>
</Modal>

With <ComposedModal>, you can control the buttons with the following code.

Button group variantUsage
No button (passive modal)Use <ComposedModal> without <ModalFooter>
One buttonUse primaryButtonText in <ModalFooter>
Two buttonsUse primaryButtonText and secondaryButtonText in <ModalFooter>
Three buttonsPut three buttons as children of <ModalFooter>, instead of using primaryButtonText or secondaryButtonText. Using this option requires style adjustment defined in application-level CSS for <ModalFooter>, e.g. .bx--modal-footer { padding: 25%; }.
Two buttons with danger buttonSet danger prop in <ModalFooter> (like below)
<ComposedModal>
...
<ModalFooter danger primaryButtonText="OK" secondaryButtonText="Cancel" />
</ComposedModal>
<ComposedModal>
...
<ModalFooter>
<Button
kind="secondary"
onClick={() => { (Run some action...) setOpen(false); }}>
Another button
</Buttton>
<Button

Using modal title as message

For short, direct messages the title can include the whole message to add visual clarity to an otherwise repetitive title and body message.

To use this pattern with <Modal>, you can omit the children prop.

const modalHeadding =
'Are you sure you want to add the "Speech to Text" service ' +
'to the node-test app?';
...
<Modal
modalHeading={modalHeading}
secondaryButtonText="Cancel"
primaryButtonText="Add"
/>

To use this pattern with <ComposedModal>, you can omit <ModalBody>.

<ComposedModal>
<ModalHeader label="Modla label">
<h1>
Are you sure you want to add the "Speech to Text" service to the node-test
app?
</h1>
</ModalHeader>
<ModalFooter primaryButtonText="OK" secondaryButtonText="Cancel" />
</ComposedModal>

Focus management

For both modal variants, once the modal is open, keyboard focus will be restricted inside modal. This means:

  • If you press Tab at the last keyboard-focusable element in modal, the first keyboard-focusable element in modal will get focus.
  • If you press Shift-Tab at the first keyboard-focusable element in the modal, the last keyboard-focusable element in the modal will get focus.

We take extra step here to ensure such behavior works with floating menus, given floating menu is placed outside of modal in DOM. If you use any non-Carbon floating menus in your application, set selectorsFloatingMenus={['.bx--overflow-menu-options', '.bx--tooltip', '.flatpickr-calendar', '.your-floating-menu-foo', '.your-floating-menu-bar']} to <Modal>/<ComposedModal>.

Also for both modal variants, you can set the DOM element that gets focus when the modal gets open, by either of the following ways:

Setting data-modal-primary-focus attribute to the target element


<ComposedModal>
<ModalBody hasForm>
<TextInput data-modal-primary-focus labelText="Enter something" />
</ModalBody>
</ComposedModal>

Specifying a query selector to the target element


{
/* `.bx--text-input` selects the `<input>` in `<TextInput>` */
}
<ComposedModal selectorPrimaryFocus=".bx--text-input">
<ModalBody hasForm>
<TextInput labelText="Enter something" />
</ModalBody>
</ComposedModal>;

Component API

PropTypeRequiredDefaultDescription
aria-labelstringrequired if hasScrollingContent is providedRequired props for the accessibility label of the header
childrennodeProvide the contents of your Modal
classNamestringSpecify an optional className to be applied to the modal root node
dangerbooleanSpecify whether the Modal is for dangerous actions
hasFormbooleanProvide whether the modal content has a form element. If true is used here, non-form child content should have bx--modal-content__regular-content class.
hasScrollingContentbooleanfalseSpecify whether the modal contains scrolling content
iconDescriptionstring‘Close’Provide a description for “close” icon that can be read by screen readers
idstringSpecify the DOM element ID of the top-level node
modalAriaLabelstringSpecify a label to be read by screen readers on the modal root node
modalHeadingnodeSpecify the content of the modal header title
modalLabelnodeSpecify the content of the modal header label
onKeyDownfunctionSpecify a handler for keypresses
onRequestSubmitfunctionSpecify a handler for “submitting” modal. The handler should care of closing modal, e.g. changing open prop, if necessary.
onRequestClosefunctionSpecify a handler for closing modal. The handler should care of closing modal, e.g. changing open prop.
openbooleanSpecify whether the Modal is currently open
onSecondarySubmitfunctionSpecify a handler for the secondary button. Useful if separate handler from onRequestClose is desirable
passiveModalbooleanfalseSpecify whether the modal should be button-less
primaryButtonDisabledbooleanfalseSpecify whether the Button should be disabled, or not
primaryButtonTextstringSpecify the text for the secondary button
secondaryButtonTextstringSpecify the text for the secondary button
selectorPrimaryFocusstring[data-modal-primary-focus]Specify a CSS selector that matches the DOM element that should be focused when the Modal opens
selectorsFloatingMenusstring[]Specify CSS selectors that match DOM elements working as floating menus. Focusing on those elements won’t trigger “focus-wrap” behavior
shouldSubmitOnEnterbooleanSpecify if Enter key should be used as “submit” action
size'xs' | 'md' | 'lg'Specify the size variant

Click the Show Info button in our storybook, or follow the links in the component live demo for other frameworks.

Feedback

Help us improve this component by providing feedback, asking questions, and leaving any other comments on GitHub.