Modal
The modal component provides a solid foundation for creating dialogs, popovers, lightboxes, or whatever else.
Introduction
Joy UI provides three modal-related components:
Modal
: A container that renders itschildren
node in front of a backdrop component.ModalClose
: A button for closing the modal.ModalDialog
: A component for rendering a modal dialog.
<Modal>
<ModalDialog>
<ModalClose />
<Typography>Modal title</Typography>
</ModalDialog>
</Modal>
Playground
Features
- 🥞 Manages modal stacking when more than one is needed.
- 🪟 Automatically creates a backdrop element to disable interaction with the rest of the app.
- 🔐 Disables page scrolling while open.
- ⌨️ Manages focus correctly between the modal and its parent app.
- ♿️ Adds the appropriate ARIA roles automatically.
Component
After installation, you can start building with this component using the following basic elements:
import Modal from '@mui/joy/Modal';
export default function MyApp() {
return <Modal>{children}</Modal>;
}
Basic usage
The Modal
accepts only a single React element as a child.
That can be either a Joy UI component, e.g. Sheet
, or any other custom element.
Use the ModalClose
component to render a close button that inherits the modal's onClose
function.
Close reason
The second argument of the onClose
gives you the information about how the event is triggered.
The possible values are:
backdropClick
: the user clicks on the modal's backdrop.escapeKeyDown
: the user pressesEscape
on the keyboard.closeClick
: the user clicks on theModalClose
element.
Dialog
To create a modal dialog, renders the ModalDialog
component inside the Modal
.
It will apply spacing to the elements that have aria-labelledby
or aria-describedby
attribute.
Layout
The ModalDialog
's layout can be:
center
(default): the modal dialog appears at the center of the viewport.fullScreen
: the modal dialog covers the whole viewport.
To add more layout, apply a style to the theme like this:
// Add a new `top` layout to the ModalDialog
extendTheme({
components: {
JoyModalDialog: {
defaultProps: {
layout: 'top',
},
styleOverrides: {
root: ({ ownerState }) => ({
...(ownerState.layout === 'top' && {
top: '12vh',
left: '50%',
transform: 'translateX(-50%)',
}),
}),
},
},
},
});
For TypeScript, you need module augmentation to include the new values to the layout
prop:
// at the root or theme file
declare module '@mui/joy/ModalDialog' {
interface ModalDialogPropsLayoutOverrides {
top: true;
}
}
Variant
The ModalDialog
supports the global variants feature.
The ModalClose
's variant adapts automatically to have a proper contrast to the ModalDialog
.
Size
The ModalDialog
comes with 3 sizes, sm
, md
(default), and lg
.
The ModalClose
and ModalDialogTitle
inherits the size from the ModalDialog
unless it is specified in each component directly.
Alert Dialog
Use role="alertdialog"
to create an alert dialog that interrupts the user's workflow.
Transition
The modal components do not come with built-in transitions.
Here is one example using react-transition-group
to create a fade animation:
Performance
The modal's content is unmounted when it is not open. This means that it will need to be re-mounted each time it is opened.
If you are rendering "expensive" component trees inside your modal, and you want to optimize for interaction responsiveness, change the default behavior by enabling the keepMounted
prop.
Use the keepMounted
prop to make the content of the modal available to search engines (even when the modal is closed).
The following demo shows how to apply this prop to keep the modal mounted:
As with any performance optimization, the keepMounted
prop won't necessarily solve all of your problems.
Explore other possible bottlenecks in performance where you could make more considerable improvements before implementing this prop.
Server-side modal
React doesn't support the createPortal()
API on the server.
Therefore, in order to display a modal rendered on the server, disable the portal feature with the disablePortal
prop, as shown in the following demo:
Server-side modal
If you disable JavaScript, you will still see me.
Limitations
Focus trap
ModalUnstyled
moves the focus back to the body of the component if the focus tries to escape it.
This is done for accessibility purposes, but it can potentially create issues for your users.
If the user needs to interact with another part of the page-for example, to interact with a chatbot window while a modal is open in the parent app-you can disable the default behavior with the disableEnforceFocus
prop.
Accessibility
See the WAI-ARIA guide on the Dialog (Modal) pattern for complete details on accessibility best practices. Here are a couple of highlights:
All interactive elements must have an accessible name. Use the
aria-labelledby="id..."
to give yourModal
component an accessible name. You can also usearia-describedby="id..."
to provide a description of theModal
:<Modal aria-labelledby="modal-title" aria-describedby="modal-description"> <h2 id="modal-title">My Title</h2> <p id="modal-description">My Description</p> </Modal>
Follow the WAI-ARIA authoring practices to help you set the initial focus on the most relevant element based on the content of the modal.