focus-trap
A tiny vanilla-JS library that traps keyboard focus inside a DOM node for accessible modals and dialogs
Repository Health
Technical Analysis
focus-trap is a small, dependency-light JavaScript module that constrains Tab and Shift+Tab navigation to a set of DOM containers, preventing keyboard users from tabbing out of a modal, drawer, or menu while it is open. It builds on tabbable to compute the current set of focusable elements, restores focus to the previously focused element on deactivation, and exposes a rich lifecycle-hook API (onActivate, onPostActivate, onDeactivate, onPostDeactivate, pause/unpause) so higher-level component libraries can wire it into their own animation and rendering cycles.
Because it operates purely on the DOM rather than any particular UI framework, focus-trap is typically consumed through a thin wrapper such as focus-trap-react rather than being called directly, though it works perfectly well framework-free. It is one of the most widely depended-upon building blocks for meeting WCAG 2.1 keyboard-trap and modal-dialog accessibility criteria in the JavaScript ecosystem.
What You Get
- createFocusTrap(element, options) factory that wraps one or more DOM containers into a single trap
- Automatic tab-order detection via the tabbable library, including Shadow DOM support
- Configurable initialFocus, fallbackFocus, and setReturnFocus targets for precise focus control
- A full lifecycle-hook API (onActivate/onPostActivate/onDeactivate/onPostDeactivate/onPause/onUnpause) for coordinating with modal animations
- pause()/unpause() support for nesting multiple focus traps (e.g. a confirm dialog inside a modal)
- TypeScript type definitions and both ESM/CJS/UMD builds
Common Use Cases
- Trapping focus inside an accessible modal or dialog while it is open
- Containing focus within a slide-out drawer, off-canvas menu, or command palette
- Building framework-specific wrappers (React, Vue, Svelte) around a single vanilla-JS focus implementation
- Coordinating focus handoff between nested overlays such as a confirmation prompt opened from within a modal
Under The Hood
Architecture - The core lives in a single index.js module exporting createFocusTrap(elements, options). Internally it normalizes the elements argument into an array of “trap containers”, tracks the currently active container set in a closure-scoped state object, and attaches document-level focusin, keydown, click, and pointerdown listeners that redirect focus back inside the trap whenever it would otherwise escape. Activation/deactivation is modeled as an explicit state machine (activate/deactivate/pause/unpause) so nested traps can suspend one another cleanly.
Tech Stack - Written in plain ES2017+ JavaScript with a single runtime dependency, tabbable, used to compute the DOM’s current tab order (including Shadow DOM roots). The project builds ESM, CJS, and UMD bundles via Rollup/Babel, ships hand-written index.d.ts TypeScript declarations rather than generated ones, and has zero DOM-framework dependency, which is what lets it work identically in React, Vue, Svelte, or framework-free code.
Code Quality - The test/suites directory contains an extensive Jest + jsdom unit-test suite plus a Cypress end-to-end suite (test:e2e) that exercises real browser tab/click behavior, and CI runs format/lint/type-check/unit/e2e on every push. Options are validated defensively (e.g. throwing when a trap has no tabbable node and no fallbackFocus), and the lifecycle-hook contract is documented exhaustively in the README and mirrored in the type definitions.
API Design - The public surface is a single factory function returning a trap object with activate(), deactivate(), pause(), and unpause() methods, plus a dozen well-named configuration options (initialFocus, fallbackFocus, escapeDeactivates, clickOutsideDeactivates, allowOutsideClick) that each accept a value, selector string, or function for maximum flexibility. Getting started requires only createFocusTrap(el).activate(), while advanced lifecycle coordination is opt-in via the hook callbacks.
Used by 7 apps in this directory
Directus
CMS · Low Code Platforms
Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.
Forem
Community · Blogging
Open source Ruby on Rails platform for building developer communities with articles, discussions, and social profiles—the same software that powers dev.to.
OnetimeSecret
Security
Generate self-destructing single-use links to share passwords and sensitive data without leaving traces in inboxes or chat logs.
Open WebUI
AI Assistants · AI Agents
The extensible, privacy-first AI platform that runs Ollama, OpenAI, and any LLM backend behind a polished, feature-packed web interface.
Scalar
Developer Tools
Beautiful, interactive OpenAPI documentation with a built-in offline-first API client and multi-language code generation — all in one open-source platform.
Sentry
Security · Developer Tools · Monitoring
Developer-first error tracking and performance monitoring platform with AI-powered root-cause analysis across 20+ languages and frameworks.
solidtime
Productivity · Invoicing Finance
Modern open-source time tracker for freelancers and agencies with invoicing, multi-org support, and Toggl/Clockify migration built in.