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 themodal</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 (<><Modalsize={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><Modalsize={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><Buttonsize={ButtonSize.small}onClick={() => setShowSmallModal(true)}ref={smallButtonRef}>Show Small Modal<VisuallyHidden>(opens modal dialog)</VisuallyHidden></Button><Buttonsize={ButtonSize.large}onClick={() => setShowLargeModal(true)}ref={largeButtonRef}>Show Large Modal<VisuallyHidden>(opens modal dialog)</VisuallyHidden></Button></ButtonGroup></>);}
Modal Header
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.
When a reference to the header is needed, use the headerRef prop. When using a custom header, you must also use tabIndex={-1}
property.
import React from 'react';import {Button,ButtonColor,Modal,Flex,FlexBehavior,FlexDirection,FlexAlignItems,FlexJustify,FlexWrap,magma,ModalSize,Paragraph,VisuallyHidden,ButtonGroup,ButtonGroupAlignment,} from 'react-magma-dom';import { CheckIcon } from 'react-magma-icons';export function Example() {const [showModal, setShowModal] = React.useState(false);const [showModalHeader, setShowModalHeader] = React.useState(false);const [showModalHeaderRef, setShowModalHeaderRef] = React.useState(false);const buttonRef = React.useRef();const headerButtonRef = React.useRef();const headerRefButtonRef = React.useRef();const [customHeadingRef, setCustomHeadingRef] = React.useState(React.useRef<any>());const handleGetHeaderRef = ref => {setCustomHeadingRef(ref);};return (<><ModalariaLabel="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><Modalheader="This modal has a header"size={ModalSize.small}onClose={() => {setShowModalHeader(false);headerButtonRef.current.focus();}}isOpen={showModalHeader}><Paragraph noTopMargin>This modal has a header.</Paragraph><ButtonGroup alignment={ButtonGroupAlignment.center}><Button onClick={() => setShowModalHeader(false)}>OK</Button></ButtonGroup></Modal><ModalonClose={() => {setShowModalHeaderRef(false);headerRefButtonRef.current.focus();}}isOpen={showModalHeaderRef}headerRef={handleGetHeaderRef}style={{ padding: '20px' }}><Flexbehavior={FlexBehavior.container}direction={FlexDirection.column}alignItems={FlexAlignItems.center}justify={FlexJustify.center}spacing={2}wrap={FlexWrap.nowrap}><CheckIconstyle={{background: magma.colors.success,color: magma.colors.neutral100,width: '36px',height: '36px',padding: '8px',borderRadius: '50%',}}/><h2 ref={customHeadingRef} tabIndex={-1}>Confirmation header</h2><Paragraph>With watermelon ostriches. Gourds utters at welding equipment a oinkoink haybine. Goose hammers cattle rats in crows. Blue berriespigeons buzz and bean prairie dogs nails at est.</Paragraph><ButtonGroup><ButtononClick={() => customHeadingRef.current.focus()}color={ButtonColor.subtle}>Focus Heading</Button><ButtononClick={() => setShowModalHeaderRef(false)}color={ButtonColor.subtle}>Close</Button></ButtonGroup></Flex></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><Buttonstyle={{ marginLeft: '0' }}onClick={() => setShowModalHeaderRef(true)}ref={headerRefButtonRef}>Show Modal with headerRef</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 (<><Modalheader="Modal Title"isCloseButtonHiddenonClose={() => {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);const [mainHeaderRef, setmainHeaderRef] = React.useState(React.useRef<any>());const buttonRef = React.useRef<HTMLButtonElement>();const handleGetHeaderRef = ref => {setmainHeaderRef(ref);};const closeModal = () => {setShowModal(false);buttonRef.current && buttonRef.current.focus();};const closeModal2 = () => {setShowModal2(false);mainHeaderRef.current && mainHeaderRef.current.focus();};return (<><Modalheader="Modal Title"onClose={() => closeModal()}isOpen={showModal}headerRef={handleGetHeaderRef}><Paragraph noTopMargin>This is a modal, doing modal things.</Paragraph><Button onClick={() => setShowModal2(true)}>Show Modal 2</Button></Modal><Button onClick={() => setShowModal(true)} ref={buttonRef}>Show Modal<VisuallyHidden>(opens modal dialog)</VisuallyHidden></Button><Modalsize={ModalSize.small}header="Modal 2 Title"onClose={() => closeModal2()}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,} from 'react-magma-dom';export function Example() {const [showModal, setShowModal] = React.useState(false);const [showConfirmationModal, setShowConfirmationModal] =React.useState(false);const buttonRef = React.useRef<HTMLButtonElement>(null);const mainModalRef = React.useRef<HTMLDivElement>(null);const confirmationModalRef = React.useRef<HTMLDivElement>(null);const [mainHeaderRef, setmainHeaderRef] = React.useState(React.useRef<any>());const handleGetHeaderRef = ref => {setmainHeaderRef(ref);};const closeTheModal = () => {setShowConfirmationModal(true);confirmationModalRef.current && confirmationModalRef.current.focus();};const closeTheConfirmationModal = () => {mainHeaderRef.current && mainHeaderRef.current.focus();setShowConfirmationModal(false);};const closeBothModals = () => {buttonRef.current && buttonRef.current.focus();setShowConfirmationModal(false);setShowModal(false);};return (<><Button onClick={() => setShowModal(true)} ref={buttonRef}>Show Modal</Button><Modalheader="Modal Title"isModalClosingControlledManuallyonClose={closeTheModal}isOpen={showModal}ref={mainModalRef}headerRef={handleGetHeaderRef}><Paragraph noTopMargin>This is a modal, doing modal things.</Paragraph><Paragraph>This is <a href="/">linked text</a> in the modal</Paragraph><Comboboxid="comboboxId3"isMultilabelText="Multi Combobox"defaultItems={[{ label: 'Red', value: 'red' },{ label: 'Blue', value: 'blue' },{ label: 'Green', value: 'green' },]}placeholder="Hello"/></Modal><Modalsize={ModalSize.small}header="Confirmation Modal"isModalClosingControlledManuallyonClose={closeTheConfirmationModal}isOpen={showConfirmationModal}ref={confirmationModalRef}><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 (<><Modalheader="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></>);}
Modal Props
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
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
-
headerRef
Description
Function that returns reference for the header
Type
function
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