# How to Implement Focus Management with FocusTrap for Accessibility in Material-UI

> Learn how to implement focus management in Material-UI using TrapFocus for enhanced accessibility. Ensure keyboard navigation stays within modal containers and meets WAI-ARIA standards.

- Repository: [MUI/material-ui](https://github.com/mui/material-ui)
- Tags: tutorial
- Published: 2026-02-26

---

**Material-UI's `TrapFocus` component automatically confines keyboard navigation within modal containers, ensuring compliance with WAI-ARIA accessibility standards by cycling focus through interactive elements and restoring focus when the modal closes.**

Implementing focus management with FocusTrap for accessibility in Material-UI is essential for creating inclusive modal experiences that support keyboard and screen reader users. The `mui/material-ui` repository provides a robust focus-trapping implementation through the `TrapFocus` component, located in [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx), which handles focus capture, tab cycling, and restoration automatically.

## How FocusTrap Works in Material-UI

The focus-trap architecture in Material-UI consists of three coordinated systems that manage focus state across modal layers.

### TrapFocus Component Architecture

The `TrapFocus` component in [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx) serves as the core implementation. When the `open` prop is true, the component:

- **Saves the previously focused element** (the element that had focus before the modal opened) to restore it later.
- **Moves focus to the first focusable element** within the modal, or to a specific element specified by the `autoFocus` prop.
- **Listens for `Tab` and `Shift+Tab` key events** and programmatically cycles focus back to the first or last focusable element when the user attempts to tab beyond the modal boundaries.
- **Restores the previously saved focus** when the modal unmounts or the `open` prop becomes false.

### ModalManager Stack Coordination

The `ModalManager` class in [`packages/material-ui/src/Modal/ModalManager.ts`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/ModalManager.ts) maintains a stack of open modals to handle nested modal scenarios. This ensures that when multiple modals are open simultaneously, only the top-most modal receives focus-trap enforcement, preventing focus conflicts between layered dialogs.

## Implementing FocusTrap in Dialogs and Modals

Material-UI's high-level components automatically compose `TrapFocus`, making standard implementation straightforward.

### Basic Dialog Implementation

The `Dialog` component in [`packages/material-ui/src/Dialog/Dialog.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Dialog/Dialog.tsx) inherits focus-trap behavior from `Modal`. When you render a `Dialog`, focus management works automatically:

```tsx
import * as React from 'react';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';

export default function AccessibleDialog() {
  const [open, setOpen] = React.useState(false);

  return (
    <>
      <Button variant="outlined" onClick={() => setOpen(true)}>
        Open dialog
      </Button>

      <Dialog
        open={open}
        onClose={() => setOpen(false)}
        aria-labelledby="dialog-title"
        aria-describedby="dialog-description"
      >
        <DialogTitle id="dialog-title">
          Accessible Dialog
        </DialogTitle>
        <DialogContent id="dialog-description">
          This dialog automatically traps focus inside while open, 
          cycling through interactive elements and restoring 
          focus to the trigger button on close.
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setOpen(false)}>Close</Button>
        </DialogActions>
      </Dialog>
    </>
  );
}

```

The `Dialog` automatically applies `role="dialog"` and composes `TrapFocus` internally, ensuring keyboard users cannot tab out of the modal while it is open.

### Disabling FocusTrap for Non-Modal Components

Some components like `Popover` in [`packages/material-ui/src/Popover/Popover.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Popover/Popover.tsx) disable the focus trap by default using the `disableEnforceFocus` prop, allowing users to interact with the underlying page. You can apply this pattern when building non-modal overlays:

```tsx
import * as React from 'react';
import Popover from '@mui/material/Popover';
import Button from '@mui/material/Button';

export default function NonModalPopover() {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const open = Boolean(anchorEl);

  return (
    <>
      <Button 
        aria-describedby={open ? 'non-modal-popover' : undefined}
        variant="contained" 
        onClick={handleClick}
      >
        Show popover
      </Button>

      <Popover
        id="non-modal-popover"
        open={open}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        disableEnforceFocus  // Focus is NOT trapped
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'center',
        }}
      >
        <div style={{ padding: 16 }}>
          This popover does not trap focus; users can continue 
          navigating the underlying page while it remains visible.
        </div>
      </Popover>
    </>
  );
}

```

## Using TrapFocus Directly in Custom Components

For custom modal implementations that do not use `Dialog` or `Modal`, import `TrapFocus` directly from the Modal module:

```tsx
import * as React from 'react';
import { TrapFocus } from '@mui/material/Modal';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

export default function CustomFocusTrap() {
  const [open, setOpen] = React.useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>
        Open custom modal
      </Button>

      {open && (
        <TrapFocus
          disableAutoFocus={false}
          disableEnforceFocus={false}
          isEnabled={() => true}
          open={open}
        >
          <Box
            sx={{
              position: 'fixed',
              top: '50%',
              left: '50%',
              width: 300,
              p: 2,
              bgcolor: 'background.paper',
              boxShadow: 24,
              transform: 'translate(-50%, -50%)',
            }}
            role="dialog"
            aria-modal="true"
            aria-labelledby="custom-modal-title"
            tabIndex={-1}
          >
            <h2 id="custom-modal-title">Custom Focus Trap</h2>
            <p>Focus is confined inside this container.</p>
            <Button onClick={() => setOpen(false)}>Close</Button>
          </Box>
        </TrapFocus>
      )}
    </>
  );
}

```

When using `TrapFocus` directly, ensure you set `role="dialog"` and `aria-modal="true"` on the container to communicate the modal context to assistive technologies.

## Key Source Files in Material-UI

Understanding the source architecture helps when debugging focus behavior or extending functionality:

| File | Purpose |
|------|---------|
| [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx) | Core focus-trap implementation handling focus capture, tab cycling, and restoration. |
| [`packages/material-ui/src/Modal/ModalManager.ts`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/ModalManager.ts) | Manages the stack of open modals to coordinate focus behavior across nested layers. |
| [`packages/material-ui/src/Modal/Modal.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/Modal.tsx) | Generic modal component that composes `TrapFocus` and provides `disableEnforceFocus` prop. |
| [`packages/material-ui/src/Dialog/Dialog.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Dialog/Dialog.tsx) | High-level dialog component built on `Modal` with automatic ARIA roles and focus trapping. |
| [`packages/material-ui/src/Popover/Popover.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Popover/Popover.tsx) | Example component that sets `disableEnforceFocus` by default for non-modal behavior. |

## Summary

- **TrapFocus** in [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx) automatically confines keyboard navigation within modal containers, saving and restoring focus to maintain accessibility.
- **Dialog** and **Modal** components include focus trapping by default, requiring no additional configuration for standard use cases.
- Use the **`disableEnforceFocus`** prop when building non-modal overlays like tooltips or popovers where users should interact with the underlying page.
- Import **TrapFocus** directly from `@mui/material/Modal` for custom modal implementations that require fine-grained focus control.
- Always include **`role="dialog"`**, **`aria-modal="true"`**, and labeling attributes to ensure screen readers correctly announce modal context.

## Frequently Asked Questions

### What is the TrapFocus component in Material-UI?

The `TrapFocus` component is a low-level utility exported from `@mui/material/Modal` that manages focus within a contained DOM subtree. According to the source code in [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx), it captures focus when activated, cycles Tab navigation through focusable elements, and restores focus to the previously active element when deactivated, ensuring compliance with WCAG guidelines for modal dialogs.

### How do I disable focus trapping in a Material-UI Dialog?

To disable focus trapping, pass the `disableEnforceFocus` prop to the `Dialog` or underlying `Modal` component. As implemented in [`packages/material-ui/src/Modal/Modal.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/Modal.tsx), this boolean prop prevents the `TrapFocus` mechanism from intercepting Tab key events, allowing users to navigate outside the modal. This is useful for non-modal popovers or when implementing custom focus management.

### Why does focus trap automatically in Material-UI Dialogs but not in Popovers?

Material-UI's `Dialog` component in [`packages/material-ui/src/Dialog/Dialog.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Dialog/Dialog.tsx) is designed as a modal window that blocks interaction with the rest of the page, so it enables focus trapping by default through the underlying `Modal` component. Conversely, `Popover` in [`packages/material-ui/src/Popover/Popover.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Popover/Popover.tsx) sets `disableEnforceFocus` to `true` by default because popovers are typically non-modal overlays that allow users to continue interacting with the underlying page content while the popover remains visible.

### Can I use TrapFocus outside of Dialog and Modal components?

Yes, you can import `TrapFocus` directly from `@mui/material/Modal` to create custom accessible components. As shown in [`packages/material-ui/src/Modal/TrapFocus.tsx`](https://github.com/mui/material-ui/blob/main/packages/material-ui/src/Modal/TrapFocus.tsx), the component accepts props like `open`, `disableAutoFocus`, and `disableEnforceFocus`, allowing you to wrap any JSX element to create a focus-managed region. When using it directly, remember to add appropriate ARIA attributes like `role="dialog"` and `aria-modal="true"` to the container element to ensure screen readers announce the modal context correctly.