focus-trap-react
A lightweight React component that traps and manages keyboard focus inside modals, dialogs, and other overlays for full keyboard accessibility.
Repository Health
Technical Analysis
focus-trap-react wraps the framework-agnostic focus-trap library in a single React component, <FocusTrap>, that keeps keyboard focus contained within a designated container. Mount it around a modal, dialog, or dropdown and it activates automatically, cycling Tab and Shift+Tab through the trap’s focusable elements and preventing focus from escaping to the rest of the page.
The component tracks the element that had focus before activation and restores it on deactivation, handles outside clicks, supports pausing and unpausing for coordinating multiple simultaneous traps, and works with either a single child container or an explicit list of containerElements. It targets React 18 and 19, ships TypeScript types out of the box, and is maintained by the same team behind the underlying focus-trap and tabbable packages it depends on.
What You Get
- A single
<FocusTrap>React component that activates on mount and deactivates on unmount by default - Automatic focus restoration to the previously focused element when the trap deactivates
- Support for
containerElementsto define trap boundaries across multiple DOM nodes instead of a single child - Full pass-through of focus-trap’s
createOptionsvia thefocusTrapOptionsprop, including click-outside and escape-key behavior - Built-in TypeScript type definitions (
index.d.ts) for the component and its props
Common Use Cases
- Building accessible modal dialogs where Tab/Shift+Tab must cycle only through the dialog’s controls
- Trapping focus inside dropdown menus or popovers while they’re open
- Implementing off-canvas navigation drawers that shouldn’t leak keyboard focus to the page behind them
- Coordinating multiple simultaneous traps (e.g. nested dialogs) using the
paused/activeprops
Under The Hood
Architecture
The library is a single class, FocusTrap (in src/focus-trap-react.js), extending React.Component and wrapping the framework-agnostic focus-trap package. It splits configuration into internalOptions (the deactivation-related options it must own internally) and originalOptions (the consumer’s original values), which lets it intercept and coordinate focus-restoration timing before delegating everything else straight to focus-trap. React lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount) drive setupFocusTrap()/deactivateTrap(), while render() either clones a single child with a callback ref to obtain its DOM node or accepts explicit containerElements for multi-node traps. There is no external state machine or DI; the whole module concentrates option normalization, deactivation sequencing, ref plumbing, and React 19 ref-shape handling into one class.
Tech Stack
Built on React (peer dependency ^18.0.0 || ^19.0.0) and depends directly on focus-trap ^8.2.2 and tabbable ^6.5.0, both maintained by the same organization. Source is authored in JavaScript and compiled with Babel (@babel/preset-env, @babel/preset-react) into dist/; TypeScript is used only for the hand-written index.d.ts declarations, checked via tsc. Tooling includes ESLint 9’s flat config with typescript-eslint, React, import, Jest, and Cypress plugins, Prettier for formatting, Jest with @testing-library/react and jsdom for unit tests, Cypress plus start-server-and-test/budo for e2e demo testing, Changesets for release management, and GitHub Actions CI with Dependabot.
Code Quality
The test suite (test/focus-trap-react.test.js) is a substantial 1,100+ line file using @testing-library/react and user-event against a real jsdom DOM, covering activation/deactivation, pause/unpause, focus restoration, outside clicks, containerElements, and React 18 Strict Mode remount behavior. Failure modes are explicit rather than swallowed — invalid focusTrapOptions values or a Fragment child throw descriptive errors instead of failing silently. Naming and comments are extensive and self-documenting, explaining non-obvious lifecycle interactions in detail. Type safety is provided by the maintained index.d.ts rather than the source itself being written in TypeScript. CI runs format, lint, coverage, type-checking, and Cypress e2e on every push.
API Design
The public surface is deliberately small — one component with five props (children, active, paused, focusTrapOptions, containerElements) — while still exposing every focus-trap option through pass-through. The library normalizes React’s ref-handling differences between React 18 and 19 internally so consumers don’t need version-specific code, and it specifically detects and recovers from React 18 Strict Mode’s dev-only unmount/remount cycle by reactivating an existing trap instance rather than leaking a duplicate. It is best understood as a thin, carefully considered React lifecycle wrapper around focus-trap rather than a novel focus-management model of its own.
Used by 6 apps in this directory
Appsmith
Developer Tools · Automation · No Code Platforms
Open-source low-code platform to build admin panels, dashboards, and internal tools connected to any database or API.
nango
Developer Tools · Automation · Authentication
Build product integrations with AI using 800+ APIs — auth, proxy, and TypeScript functions on production-grade infrastructure.
OpenHands
AI Code Assistants · AI Development
The self-hosted developer control center for running AI coding agents — locally, in Docker, on VMs, or across cloud backends — with automation workflows for GitHub, Slack, and more.
overleaf
Collaboration · Productivity
Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.
Tabby
AI Code Assistants
Self-hosted AI coding assistant — run GitHub Copilot-grade code completion on your own hardware with no cloud dependency.
ToolJet
Low Code Platforms · No Code Platforms · AI Agents
Open-source AI-native platform to build and deploy internal tools, workflows, and AI agents with a visual drag-and-drop builder and 80+ data source integrations.