Modal
A basic and commonly used modal
Modal variations
Modals can be opened or closed with a isOpen
prop.
Basic modal with a form
The Modal
has a curtain (backdrop) at medium and large breakpoints. It appears fullscreen on small devices.
function ModalDemoBasic() { const [isOpen, setIsOpen] = React.useState(false); const [licenseText, setLicenseText] = React.useState(''); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} > <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <ModalContent> <ol className="tp-form-fields"> <li className="tp-form-field__item"> <Label>License type</Label> <Dropdown value="default" isFullWidth onChange={() => {}}> <option value="default">Choose one…</option> </Dropdown> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={setLicenseText} placeholder="XX-XXXX-XXXX" value={licenseText} /> </li> </ol> </ModalContent> <ModalFooter> <ButtonRow justify="right" isStackedBelowSmall> <Button onClick={() => setIsOpen(false)} width="full-below-small"> Submit </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Tall modal with a sticky footer
This modal has a sticky footer that is always visible. The sticky footer is set by using the isSticky
prop on the ModalFooter
component.
function ModalDemoStickyTall() { const [isOpen, setIsOpen] = React.useState(false); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} > <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <ModalContent> <ol className="tp-form-fields"> <li className="tp-form-field__item"> <Label>License type</Label> <Dropdown value="default" isFullWidth onChange={() => {}}> <option value="default">Choose one…</option> </Dropdown> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> </ol> </ModalContent> <ModalFooter isSticky> <ButtonRow justify="right" isStackedBelowSmall> <Button onClick={() => setIsOpen(false)} width="full-below-small"> Submit </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Tall modal without a sticky footer
This modal does not have a default, non-sticky footer. You'll have to scroll to view the button within the ModalFooter
.
function ModalDemoTall() { const [isOpen, setIsOpen] = React.useState(false); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} > <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <ModalContent> <ol className="tp-form-fields"> <li className="tp-form-field__item"> <Label>License type</Label> <Dropdown value="default" isFullWidth onChange={() => {}}> <option value="default">Choose one…</option> </Dropdown> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> </ol> </ModalContent> <ModalFooter> <ButtonRow justify="right" isStackedBelowSmall> <Button onClick={() => setIsOpen(false)} width="full-below-small"> Submit </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Narrow modal
It's possible to render narrow modals with the width
prop.
function ModalDemoNarrow() { const [isOpen, setIsOpen] = React.useState(false); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal width="narrow" isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} > <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <ModalContent> <ol className="tp-form-fields"> <li className="tp-form-field__item"> <Label>License type</Label> <Dropdown value="default" isFullWidth onChange={() => {}}> <option value="default">Choose one…</option> </Dropdown> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> </ol> </ModalContent> <ModalFooter> <ButtonRow justify="right" isStackedBelowSmall> <Button onClick={() => setIsOpen(false)} width="full-below-small"> Submit </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Modal with fixed height
To keep the height of the modal (including top row with close button and sticky footer) fixed such that it remains consistent as modal content changes, you can apply heightAboveSmall
prop. It only applies when the viewport is above the small breakpoint; modals always stretch 100% of the viewport under small breakpoint. When used with <ModalFooter isSticky>
, the modal will not expand beyond the height of the viewport to maintain a sticky footer (i.e. the modal's height will be the max of the provided height and the viewport height).
function ModalDemoFixedHeight() { const [isOpen, setIsOpen] = React.useState(false); const [showIntroScreen, setShowIntroScreen] = React.useState(true); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} heightAboveSmall="medium" > <ModalHeader> <ModalTitle>{showIntroScreen ? 'First screen' : 'Second screen'}</ModalTitle> </ModalHeader> <ModalContent> {showIntroScreen ? ( <div> <p> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> <p> At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. </p> <p> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> <p> At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> </div> ) : ( <div> <p> Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. </p> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. </p> <p> Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. </p> <p> Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. </p> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. </p> <p> At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> <p> Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. </p> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. </p> </div> )} </ModalContent> <ModalFooter isSticky> <ButtonRow justify="right" isStackedBelowSmall> {!showIntroScreen && ( <Button onClick={() => setShowIntroScreen(true)} width="full-below-small" theme="secondary" > Go back </Button> )} <Button onClick={ showIntroScreen ? () => setShowIntroScreen(false) : () => setIsOpen(false) } width="full-below-small" > {showIntroScreen ? 'Next' : 'Submit'} </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Full bleed modal
You can use the ModalContentFullBleed
component to create a section of modal content that visually reaches the edge of the modal. This is useful for setting your own background color or section borders, for example.
Warning
function ModalDemoFullBleed() { const [isOpen, setIsOpen] = React.useState(false); return ( <React.Fragment> <Button onClick={() => { setIsOpen(true); }} > Open modal </Button> <Modal isOpen={isOpen} onCloseClick={() => { setIsOpen(false); }} onCloseFinish={() => { console.log('Modal onCloseFinish'); }} onOpenFinish={() => { console.log('Modal onOpenFinish'); }} > <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <ModalContent> <ModalContentFullBleed style={{ backgroundColor: tokens.tpColorGray200, borderTop: `1px solid ${tokens.tpColorGray}`, borderBottom: `1px solid ${tokens.tpColorGray}`, paddingTop: '20px', paddingBottom: '20px', }} > <ol className="tp-form-fields"> <li className="tp-form-field__item"> <Label>License type</Label> <Dropdown value="default" isFullWidth onChange={() => {}}> <option value="default">Choose one…</option> </Dropdown> </li> <li className="tp-form-field__item"> <Label for="example-license-number">License number</Label> <TextInput id="example-license-number" onChange={() => {}} placeholder="XX-XXXX-XXXX" /> </li> </ol> </ModalContentFullBleed> </ModalContent> <ModalFooter> <ButtonRow justify="right" isStackedBelowSmall> <Button onClick={() => setIsOpen(false)} width="full-below-small"> Submit </Button> </ButtonRow> </ModalFooter> </Modal> </React.Fragment> ); }
Props
Modal
onCloseClick
requiredFunction that fires to close the modal.
Type() => void
children
Content that appears within the modal.
TypeReact.ReactNode
onOpenFinish
Function that fires once the modal has opened and transitions have ended.
Type() => void
onCloseFinish
Function that fires once the modal has closed and transitions have ended.
Type() => void
shouldHideCloseButton
Determines if the close button should be rendered. This is generally discouraged and should be used carefully. If used, the contents passed into the modal must contain a focusable element such as a link or button.
Typeboolean
shouldCloseOnCurtainClick
Determines if the modal should close when clicking on the curtain, outside of the
children
.Typeboolean
isOpen
Should the modal appear open.
Typeboolean
width
Sets the max-width of the modal container.
Type'narrow' | 'medium' | 'wide'
heightAboveSmall
Sets height of the modal container above small viewport. If
auto
(default), the modal height will be determined by its content. Otherwise, the modal height will be fixed at some constant px.Type'auto' | 'medium' | 'tall'
ModalHeader
children
requiredContent (usually a
ModalTitle
andModalDescription
) that appears at the top of the modal.TypeReact.ReactNode
ModalTitle
children
requiredText that describes the modal contents. It is intended for use within the
ModalHeader
.Typestring
ModalDescription
children
requiredText intended for use below a
ModalTitle
and within aModalHeader
.TypeReact.ReactNode
ModalContent
children
requiredContent (usually a form) that makes up the main part of the modal.
TypeReact.ReactNode
ModalContentFullBleed
children
requiredContent (usually a form) that makes up the main part of the modal.
TypeReact.ReactNode
className
Allows the React
className
prop to be passed through to the rendered element.Typestring
Default''
style
Allows the React
style
prop to be passed through to the rendered element.TypeReact.CSSProperties
Default{}
ModalFooter
children
requiredContent (ususally buttons) to render within the footer.
TypeReact.ReactNode
isSticky
Attaches the footer to the bottom of the modal below the small breakpoint.
Typeboolean
ModalAnimatedWrapper
onCloseClick
requiredFunction that fires to close the modal.
Type() => void
children
Content that appears within the modal.
TypeReact.ReactNode
onOpenFinish
Function that fires once the modal has opened and transitions have ended.
Type() => void
onCloseFinish
Function that fires once the modal has closed and transitions have ended.
Type() => void
shouldCloseOnCurtainClick
Determines if the modal should close when clicking on the curtain, outside of the
children
.Typeboolean
Defaulttrue
shouldPageScrollAboveSmall
Allows the page to scroll vertically at viewports larger than the small breakpoint. If
false
, the modal will always be equal to or smaller than the viewport and the contents of the modal will scroll, not the page itself.Typeboolean
Defaulttrue
isOpen
Should the modal appear open.
Typeboolean
Defaultfalse
width
Sets the max-width of the modal container.
Type'narrow' | 'medium' | 'wide'
Default'medium'
heightAboveSmall
Sets height of the modal container above small viewport. If
auto
(default), the modal height will be determined by its content. Otherwise, the modal height will be fixed at some constant px.Type'auto' | 'medium' | 'tall'
Default'auto'