Focus trap
The pattern that keeps keyboard focus inside a modal dialog while it's open — preventing Tab from escaping back to the document underneath, and restoring focus to the trigger element when the dialog closes.
A focus trap is a deliberate constraint on keyboard navigation: while a modal dialog is open, Tab cycles only between the focusable elements inside the dialog. Shift+Tab goes the other way. The user cannot Tab their way back into the (visually-blocked) page behind the modal until they explicitly close it.
Focus trapping is required for accessible modal dialogs. Without it, keyboard and screen-reader users land on invisible page content they can’t see and can’t escape from.
Why this is necessary
A modal dialog is visually presented as a layer on top of the page. The intent — emphatically — is that the user interacts with the modal until they’re done, then returns to the page. For mouse users this is intuitive: clicking outside the modal does nothing (or closes it); clicking inside reaches the modal’s controls.
Without a focus trap, keyboard users get an inconsistent experience:
- Modal opens. Focus moves to (or should move to) the first focusable element inside.
- User presses Tab. Focus moves to the next focusable element inside the modal.
- User keeps pressing Tab. Focus eventually reaches the last focusable element inside the modal.
- One more Tab. Without a trap, focus jumps to the next focusable element in the document — which is somewhere behind the modal, invisible to the user.
The user’s only signal that this happened is that the modal’s focus indicator disappears. They have no idea where their focus is.
The full modal pattern
A complete accessible modal includes:
- Focus moves into the modal when it opens — typically to the
first focusable element or to the modal’s heading (if heading has
tabindex="-1"for programmatic focus). - Focus is trapped inside the modal: Tab from the last element wraps to the first; Shift+Tab from the first wraps to the last.
- Escape closes the modal and returns focus to the trigger element that opened it (not to the document body).
- Click outside the modal closes it — optional behaviour; not all modals do this, but if your modal does, it must still trap focus while open.
role="dialog"plusaria-modal="true"on the modal element, witharia-labelledbypointing at the modal’s heading.
Implementations
The most-cited library is focus-trap (npm: focus-trap,
focus-trap-react, focus-trap-vue) — tiny, dependency-free, handles
all the edge cases (skipping invisible elements, integrating with
inert, restoring focus on close).
The modern browser primitive is <dialog> with dialog.showModal(),
which automatically traps focus and handles Escape. Browser support is
now strong enough to use this as the baseline.
Anti-pattern: the “trap that doesn’t release”
A focus trap must release when the modal closes. The most common implementation bug is forgetting to restore focus to the original trigger — focus then ends up on the document body, which screen readers announce as nothing. The user is dropped into silence and has to start tabbing from the top of the page.