Skip Navigation
React Magma

Modal

Modals inform users about a task and can contain critical information, require decisions, or involve multiple tasks.

Basic Usage

The Modal component renders its children node in front of a backdrop component. It also disables scrolling of the page content while open, properly manages focus; moving to the modal content, and keeping it there until the modal is closed, and adds the appropriate ARIA roles automatically.

The modal will only render if the isOpen prop is set to true. For that reason, it is redundant to show the modal using the following anti-pattern: {flag && <Modal isOpen={flag} />. Instead, simply use <Modal isOpen={flag} />

Although not required, it is helpful to inform users (especially screen reader users) when a button or link will trigger a modal dialog. This can be done by supplementing the button label or link text with "(opens modal dialog)" using the VisuallyHidden component.

import React from 'react';
import {
Button,
Hyperlink,
Modal,
Paragraph,
VisuallyHidden,
} from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const buttonRef = React.useRef();
function handleOnClose() {
setShowModal(false);
buttonRef.current.focus();
}
return (
<>
<Modal header="Modal Title" onClose={handleOnClose} isOpen={showModal}>
<Paragraph noMargins>This is a modal, doing modal things.</Paragraph>
<Paragraph>
This is <Hyperlink to="#">linked text</Hyperlink> in the modal
</Paragraph>
<Paragraph>
<Button>This is a button</Button>
</Paragraph>
<Paragraph>
This is <Hyperlink to="#">some more linked text</Hyperlink> in the
modal
</Paragraph>
</Modal>
<Button onClick={() => setShowModal(true)} ref={buttonRef}>
Show Modal
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
</>
);
}

Sizes

Sizes for modals include small, medium, and large, with medium being the default value.

import React from 'react';
import {
Button,
ButtonSize,
Modal,
ModalSize,
Paragraph,
VisuallyHidden,
ButtonGroup,
} from 'react-magma-dom';
export function Example() {
const [showSmallModal, setShowSmallModal] = React.useState(false);
const [showLargeModal, setShowLargeModal] = React.useState(false);
const smallButtonRef = React.useRef();
const largeButtonRef = React.useRef();
return (
<>
<Modal
size={ModalSize.small}
header="Modal Small"
onClose={() => {
setShowSmallModal(false);
smallButtonRef.current.focus();
}}
isOpen={showSmallModal}
>
<Paragraph noMargins>
This is a small modal, doing small modal things.
</Paragraph>
</Modal>
<Modal
size={ModalSize.large}
header="Modal Large"
onClose={() => {
setShowLargeModal(false);
largeButtonRef.current.focus();
}}
isOpen={showLargeModal}
>
<Paragraph noMargins>
This is a large modal, doing large modal things.
</Paragraph>
</Modal>
<ButtonGroup>
<Button
size={ButtonSize.small}
onClick={() => setShowSmallModal(true)}
ref={smallButtonRef}
>
Show Small Modal
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
<Button
size={ButtonSize.large}
onClick={() => setShowLargeModal(true)}
ref={largeButtonRef}
>
Show Large Modal
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
</ButtonGroup>
</>
);
}

The modal header prop is optional. It can accept a node or a string, and will be rendered inside an H1.

If there is a header passed in, the focus will be placed on the header when the modal opens. If not, the focus will be placed on the first actionable element.

If the modal does not use the header prop, you must use the ariaLabel prop to ensure the correct aria properties are in place.

import React from 'react';
import {
Button,
Modal,
ModalSize,
Paragraph,
VisuallyHidden,
ButtonGroup,
ButtonGroupAlignment,
} from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const [showModalHeader, setShowModalHeader] = React.useState(false);
const buttonRef = React.useRef();
const headerButtonRef = React.useRef();
return (
<>
<Modal
ariaLabel="customAriaLabel"
size={ModalSize.small}
onClose={() => {
setShowModal(false);
buttonRef.current.focus();
}}
isOpen={showModal}
>
<Paragraph noTopMargin>This modal has no header.</Paragraph>
<ButtonGroup alignment={ButtonGroupAlignment.center}>
<Button onClick={() => setShowModal(false)}>OK</Button>
</ButtonGroup>
</Modal>
<Modal
header="This modal has a header"
size={ModalSize.small}
onClose={() => {
setShowModalHeader(false);
headerButtonRef.current.focus();
}}
isOpen={showModalHeader}
>
<Paragraph noTopMargin>This modal has no header.</Paragraph>
<ButtonGroup alignment={ButtonGroupAlignment.center}>
<Button onClick={() => setShowModalHeader(false)}>OK</Button>
</ButtonGroup>
</Modal>
<ButtonGroup>
<Button onClick={() => setShowModal(true)} ref={buttonRef}>
Show Modal with no header
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
<Button onClick={() => setShowModalHeader(true)} ref={headerButtonRef}>
Show Modal with header
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
</ButtonGroup>
</>
);
}

Hide Close Button

The close button can be hidden by using the isCloseButtonHidden prop. If this prop is used, it is mandatory to provide another way to close the modal.

import React from 'react';
import { Button, Modal, Paragraph } from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const buttonRef = React.useRef();
return (
<>
<Modal
header="Modal Title"
isCloseButtonHidden
onClose={() => {
setShowModal(false);
buttonRef.current.focus();
}}
isOpen={showModal}
>
<Paragraph noTopMargin>
The standard modal close button is hidden.
</Paragraph>
<Button onClick={() => setShowModal(false)}>Close this Dialog</Button>
</Modal>
<Button onClick={() => setShowModal(true)} ref={buttonRef}>
Show Modal
</Button>
</>
);
}

Custom Close Button

If you would like to add a custom close button to the Modal be sure not to use the same onClose function as it will mean that the function will be called twice (once internally and once by react-magma).

import React from 'react';
import { Button, Modal, Paragraph, VisuallyHidden } from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const [magmaCloseCalledTimes, setMagmaCloseCalledTimes] = React.useState(0);
const [internalCloseCalledTimes, setInternalCloseCalledTimes] =
React.useState(0);
const buttonRef = React.useRef();
function closeModal() {
setMagmaCloseCalledTimes(magmaCloseCalledTimes + 1);
setShowModal(false);
buttonRef.current.focus();
}
function customCloseModal() {
setInternalCloseCalledTimes(internalCloseCalledTimes + 1);
setShowModal(false);
buttonRef.current.focus();
}
return (
<>
<Modal header="Modal Title" onClose={closeModal} isOpen={showModal}>
Lorem ipsum dolar sit amet
<Button onClick={customCloseModal}>Close Modal</Button>
</Modal>
<Paragraph noMargins>
<strong>Magma Close Called Times:</strong> {magmaCloseCalledTimes}
</Paragraph>
<Paragraph>
<strong>Internal Close Called Times:</strong> {internalCloseCalledTimes}
</Paragraph>
<Button onClick={() => setShowModal(true)} ref={buttonRef}>
Show Modal <VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
</>
);
}

Nested Modals

Although we don't recommend using nested modals, the ability for one nested modal is supported. Take note of the DOM layout as two Modal's need to be siblings, otherwise mouse behavior will break.

import React from 'react';
import {
Button,
Modal,
ModalSize,
Paragraph,
VisuallyHidden,
} from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const [showModal2, setShowModal2] = React.useState(false);
return (
<>
<Modal
header="Modal Title"
onClose={() => {
setShowModal(false);
}}
isOpen={showModal}
>
<Paragraph noTopMargin>This is a modal, doing modal things.</Paragraph>
<Button onClick={() => setShowModal2(true)}>Show Modal 2</Button>
</Modal>
<Button onClick={() => setShowModal(true)}>
Show Modal
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
<Modal
size={ModalSize.small}
header="Modal 2 Title"
onClose={() => setShowModal2(false)}
isOpen={showModal2}
>
<Paragraph noMargins>This is modal 2</Paragraph>
</Modal>
</>
);
}

Close Modal With Confirmation

Show confirmation modal when trying to close the main modal.

import React from 'react';
import {
Button,
ButtonGroup,
Combobox,
Modal,
ModalSize,
Paragraph,
useFocusLock,
} from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const [showConfirmationModal, setShowConfirmationModal] =
React.useState(false);
const buttonRef = React.useRef<HTMLButtonElement>();
const focusTrapElement = useFocusLock(!showConfirmationModal && showModal);
const closeTheModal = () => {
setShowConfirmationModal(true);
};
const closeTheConfirmationModal = () => {
setShowConfirmationModal(false);
};
const closeBothModals = () => {
buttonRef.current.focus();
setShowConfirmationModal(false);
setShowModal(false);
};
return (
<>
<Button onClick={() => setShowModal(true)} ref={buttonRef}>
Show Modal
</Button>
<Modal
header="Modal Title"
isModalClosingControlledManually
onClose={closeTheModal}
isOpen={showModal}
ref={focusTrapElement}
>
<Paragraph noTopMargin>This is a modal, doing modal things.</Paragraph>
<Paragraph>
This is <a href="/">linked text</a> in the modal
</Paragraph>
<Combobox
id="comboboxId3"
isMulti
labelText="Multi Combobox"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
placeholder="Hello"
/>
</Modal>
<Modal
size={ModalSize.small}
header="Confirmation Modal"
isModalClosingControlledManually
onClose={closeTheConfirmationModal}
isOpen={showConfirmationModal}
>
<Paragraph noTopMargin>Close the modal?</Paragraph>
<ButtonGroup>
<Button onClick={closeBothModals}>Yes</Button>
<Button onClick={closeTheConfirmationModal}>No, go back</Button>
</ButtonGroup>
</Modal>
</>
);
}

isInverse

import React from 'react';
import {
Button,
Hyperlink,
Modal,
Paragraph,
VisuallyHidden,
Card,
CardBody,
} from 'react-magma-dom';
export function Example() {
const [showModal, setShowModal] = React.useState(false);
const buttonRef = React.useRef();
return (
<>
<Modal
header="Modal Title"
onClose={() => {
setShowModal(false);
buttonRef.current.focus();
}}
isOpen={showModal}
isInverse
>
<Paragraph isInverse noMargins>
This is a modal, doing modal things.
</Paragraph>
<Paragraph isInverse>
This is{' '}
<Hyperlink to="#" isInverse>
linked text
</Hyperlink>{' '}
in the modal
</Paragraph>
<Paragraph isInverse>
<Button isInverse>This is a button</Button>
</Paragraph>
<Paragraph isInverse>
This is{' '}
<Hyperlink to="#" isInverse>
some more linked text
</Hyperlink>{' '}
in the modal
</Paragraph>
</Modal>
<Card isInverse>
<CardBody>
<Button onClick={() => setShowModal(true)} ref={buttonRef} isInverse>
Show Modal
<VisuallyHidden>(opens modal dialog)</VisuallyHidden>
</Button>
</CardBody>
</Card>
</>
);
}

This component uses forwardRef. The ref is applied to the div element that wraps the Modal content.

All of the global HTML attributes can be provided as props and will be applied to the div element that wraps the modal content.

ariaLabel

Description

Custom aria label ONLY for modals that do not have a header

Type

string

Default

-


children

Required

Description

The content of the component

Type

ReactNode | undefined

Default

-


closeAriaLabel

Description

The text read by screen readers for the close button

Type

string

Default

"Close dialog"


closeButtonSize

Description

Style for the modal container

Type

function

Default

-


containerStyle

Description

Style for the modal container

Type

CSSProperties

Default

-


header

Description

The content of the modal header

Type

React.ReactNode

Default

-


isBackgroundClickDisabled

Description

If true, clicking the backdrop will not dismiss the modal

Type

boolean

Default

false


isCloseButtonHidden

Description

If true, the close button the the modal will be suppressed

Type

boolean

Default

false


isEscKeyDownDisabled

Description

If true, pressing the Escape key will not dismiss the modal

Type

boolean

Default

-


isInverse

Description

If true, the component will have inverse styling to better appear on a dark background

Type

boolean

Default

false


isModalClosingControlledManually

Description

If true, closing the modal handled on the consumer side

Type

boolean

Default

false


isOpen

Description

If true, the modal will be visible

Type

boolean

Default

false


onClose

Description

Action that fires when the close button is clicked

Type

function

Default

-


onEscKeyDown

Description

Action that fires when the Escape key is pressed

Type

function

Default

-


size

Description

The relative size of the modal

Type

enum, one of:
ModalSize.large
ModalSize.medium
ModalSize.small

Default

ModalSize.medium


unmountOnExit

Description

If true, the modal will removed from the DOM when closed

Type

boolean

Default

true


On this page

Deploys by Netlify