Trap Focus
Trap focus within a DOM node.
TrapFocus is a component that manages focus for its descendants. This is useful when implementing overlays such as modal dialogs, which should not allow the focus to escape while open.
When
open={true}
the trap is enabled, and pressing Tab or Shift+Tab will rotate focus within the inner focusable elements of the component.⚠️ The component is experimental and unstable.
Example
import * as React from 'react'; import Box from '@mui/material/Box'; import TrapFocus from '@mui/material/Unstable_TrapFocus'; export default function BasicTrapFocus() { const [open, setOpen] = React.useState(false); return ( <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column', }} > <button type="button" onClick={() => setOpen(true)}> Open </button> {open && ( <TrapFocus open> <Box tabIndex={-1} sx={{ mt: 1, p: 1 }}> <label> First name: <input type="text" /> </label> <br /> <button type="button" onClick={() => setOpen(false)}> Close </button> </Box> </TrapFocus> )} </Box> ); }
Unstyled
As the component does not have any styles, it also comes with the Base package.
import TrapFocus from '@mui/base/Unstable_TrapFocus';
Disable enforce focus
Clicks within the focus trap behave normally, but clicks outside the focus trap are blocked.
You can disable this behavior with the
disableEnforceFocus
prop.import * as React from 'react'; import Box from '@mui/material/Box'; import TrapFocus from '@mui/material/Unstable_TrapFocus'; export default function DisableEnforceFocus() { const [open, setOpen] = React.useState(false); return ( <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column', }} > <button type="button" onClick={() => setOpen(true)}> Open </button> {open && ( <TrapFocus disableEnforceFocus open> <Box tabIndex={-1} sx={{ mt: 1, p: 1 }}> <label> First name: <input type="text" /> </label> <br /> <button type="button" onClick={() => setOpen(false)}> Close </button> </Box> </TrapFocus> )} </Box> ); }
Lazy activation
By default, the component moves the focus to its descendants as soon as it opens:
open={true}
.You can disable this behavior and make it lazy with the
disableAutoFocus
prop. When auto focus is disabled, as in the demo below, the component only traps the focus once it gets focused.import * as React from 'react'; import Box from '@mui/material/Box'; import TrapFocus from '@mui/material/Unstable_TrapFocus'; export default function LazyTrapFocus() { const [open, setOpen] = React.useState(false); return ( <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column', }} > <button type="button" onClick={() => setOpen(true)}> Open </button> {open && ( <TrapFocus open disableAutoFocus> <Box tabIndex={-1} sx={{ mt: 1, p: 1 }}> <label> First name: <input type="text" /> </label> <br /> <button type="button" onClick={() => setOpen(false)}> Close </button> </Box> </TrapFocus> )} </Box> ); }
Portal
The following demo uses the
Portal
component to render a subset of the trap focus children into a new "subtree" outside of the current DOM hierarchy; so that they no longer form part of the focus loop.import * as React from 'react'; import Box from '@mui/material/Box'; import Portal from '@mui/material/Portal'; import TrapFocus from '@mui/material/Unstable_TrapFocus'; export default function PortalTrapFocus() { const [open, setOpen] = React.useState(false); const [container, setContainer] = React.useState(null); return ( <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column', }} > <button type="button" onClick={() => setOpen(true)}> Open </button> {open && ( <TrapFocus open> <Box tabIndex={-1} sx={{ mt: 1, p: 1 }}> <label> First name: <input type="text" /> </label> <br /> <Portal container={container}> <label> Last name: <input type="text" /> </label> <br /> </Portal> <button type="button" onClick={() => setOpen(false)}> Close </button> </Box> </TrapFocus> )} <div ref={setContainer} /> </Box> ); }