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
onCloseClickrequiredFunction that fires to close the modal.
Type() => voidchildrenContent that appears within the modal.
TypeReact.ReactNodeonOpenFinishFunction that fires once the modal has opened and transitions have ended.
Type() => voidonCloseFinishFunction that fires once the modal has closed and transitions have ended.
Type() => voidshouldHideCloseButtonDetermines 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.
TypebooleanshouldCloseOnCurtainClickDetermines if the modal should close when clicking on the curtain, outside of the
children.TypebooleanisOpenShould the modal appear open.
TypebooleanwidthSets the max-width of the modal container.
Type'narrow' | 'medium' | 'wide'heightAboveSmallSets 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
childrenrequiredContent (usually a
ModalTitleandModalDescription) that appears at the top of the modal.TypeReact.ReactNode
ModalTitle
childrenrequiredText that describes the modal contents. It is intended for use within the
ModalHeader.Typestring
ModalDescription
childrenrequiredText intended for use below a
ModalTitleand within aModalHeader.TypeReact.ReactNode
ModalContent
childrenrequiredContent (usually a form) that makes up the main part of the modal.
TypeReact.ReactNode
ModalContentFullBleed
childrenrequiredContent (usually a form) that makes up the main part of the modal.
TypeReact.ReactNodeclassNameAllows the React
classNameprop to be passed through to the rendered element.TypestringDefault''styleAllows the React
styleprop to be passed through to the rendered element.TypeReact.CSSPropertiesDefault{}
ModalFooter
childrenrequiredContent (ususally buttons) to render within the footer.
TypeReact.ReactNodeisStickyAttaches the footer to the bottom of the modal below the small breakpoint.
Typeboolean
ModalAnimatedWrapper
onCloseClickrequiredFunction that fires to close the modal.
Type() => voidchildrenContent that appears within the modal.
TypeReact.ReactNodeonOpenFinishFunction that fires once the modal has opened and transitions have ended.
Type() => voidonCloseFinishFunction that fires once the modal has closed and transitions have ended.
Type() => voidshouldCloseOnCurtainClickDetermines if the modal should close when clicking on the curtain, outside of the
children.TypebooleanDefaulttrueshouldPageScrollAboveSmallAllows 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.TypebooleanDefaulttrueisOpenShould the modal appear open.
TypebooleanDefaultfalsewidthSets the max-width of the modal container.
Type'narrow' | 'medium' | 'wide'Default'medium'heightAboveSmallSets 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'