{"id":15109,"library":"focus-trap","title":"Accessible Focus Trapping","description":"focus-trap is a vanilla JavaScript library designed to trap keyboard focus within a specified DOM node, essential for building accessible UI components like modals, dialogs, and sidebars. It ensures that users navigating with keyboard (Tab, Shift+Tab) or screen readers cannot escape the designated area, enhancing accessibility. The current stable version is 8.0.1, with releases occurring as needed for bug fixes (patch), new features (minor), and breaking changes (major). Key differentiators include its lightweight, framework-agnostic nature, robust handling of nested traps (pausing/unpausing), and its reliance on the well-maintained `tabbable` library for determining focusable elements. It handles initial focus, tabbing within the trap, blocking clicks outside, and restoring focus on deactivation. It explicitly supports modern desktop browsers and offers UMD builds for environments without bundlers.","status":"active","version":"8.0.1","language":"javascript","source_language":"en","source_url":"https://github.com/focus-trap/focus-trap","tags":["javascript","focus","accessibility","trap","capture","keyboard","modal","typescript"],"install":[{"cmd":"npm install focus-trap","lang":"bash","label":"npm"},{"cmd":"yarn add focus-trap","lang":"bash","label":"yarn"},{"cmd":"pnpm add focus-trap","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used internally to determine the order of focusable elements within the trap, crucial for proper keyboard navigation.","package":"tabbable","optional":false}],"imports":[{"note":"This library uses named exports. There is no default export for the primary function.","wrong":"import createFocusTrap from 'focus-trap';","symbol":"createFocusTrap","correct":"import { createFocusTrap } from 'focus-trap';"},{"note":"Import the type definition for the FocusTrap instance if working with TypeScript.","symbol":"FocusTrap","correct":"import type { FocusTrap } from 'focus-trap';"},{"note":"While CommonJS `require` works, the primary export is named. ESM imports are preferred in modern Node.js and bundled environments.","wrong":"const createFocusTrap = require('focus-trap');","symbol":"createFocusTrap (CommonJS)","correct":"const { createFocusTrap } = require('focus-trap');"},{"note":"When using the UMD build from unpkg, `tabbable` must be included separately and *before* focus-trap. The library exposes its named exports on `window.FocusTrap`.","symbol":"createFocusTrap (UMD)","correct":"<!-- Include tabbable first -->\n<script src=\"https://unpkg.com/tabbable/dist/index.umd.js\"></script>\n<script src=\"https://unpkg.com/focus-trap/dist/focus-trap.umd.js\"></script>\n<script>\n  const { createFocusTrap } = window.FocusTrap;\n</script>"}],"quickstart":{"code":"import { createFocusTrap } from 'focus-trap';\n\nconst modalElement = document.getElementById('my-modal');\nconst openButton = document.getElementById('open-modal-button');\nconst closeButton = document.getElementById('close-modal-button');\n\nlet focusTrapInstance: ReturnType<typeof createFocusTrap> | null = null;\n\nfunction openModal() {\n  if (modalElement) {\n    modalElement.style.display = 'block';\n    focusTrapInstance = createFocusTrap(modalElement, {\n      onDeactivate: () => {\n        if (modalElement) modalElement.style.display = 'none';\n        openButton?.focus(); // Return focus to the element that opened the modal\n      },\n      initialFocus: closeButton || undefined, // Optionally focus the close button first\n    });\n    focusTrapInstance.activate();\n  }\n}\n\nfunction closeModal() {\n  if (focusTrapInstance) {\n    focusTrapInstance.deactivate();\n    focusTrapInstance = null;\n  }\n}\n\nopenButton?.addEventListener('click', openModal);\ncloseButton?.addEventListener('click', closeModal);\n\n// Example: simulate opening a modal after a delay\nsetTimeout(openModal, 1000);","lang":"typescript","description":"Demonstrates creating, activating, and deactivating a focus trap for a modal dialog, including managing visibility and returning focus."},"warnings":[{"fix":"Review existing `onPostActivate()` implementations. If you relied on the previous, incorrect timing, you may need to adjust your logic or use `onActivate()` if you need a callback before focus is set.","message":"The `onPostActivate()` callback now correctly executes *after* the initial focus node has received focus and the trap is fully activated. Previously, it would fire prematurely. This aligns the behavior with the intended purpose of `onPostActivate()`.","severity":"breaking","affected_versions":">=8.0.0"},{"fix":"Users and developers must enable 'Preferences > Advanced > Press Tab to highlight each item on a webpage' in Safari settings for focus traps to function correctly.","message":"In Safari, the default browser settings prevent tabbing through all elements on a webpage. This significantly impacts how `focus-trap` determines and cycles through tabbable elements, potentially causing traps to not work as expected.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Avoid using `focus-trap` in projects requiring IE compatibility. Consider polyfills or alternative strategies for legacy browser support if absolutely necessary, but it's generally recommended to drop IE support.","message":"Support for Internet Explorer (all versions) has been officially dropped due to Microsoft's end-of-life for IE. The library no longer guarantees functionality or provides fixes for IE-specific issues.","severity":"deprecated","affected_versions":">=7.6.4"},{"fix":"Ensure that `<script src=\"https://unpkg.com/tabbable/dist/index.umd.js\"></script>` is placed in your HTML *before* `<script src=\"https://unpkg.com/focus-trap/dist/focus-trap.umd.js\"></script>`.","message":"When using the UMD build (`dist/focus-trap.umd.js`) directly in the browser without a module bundler, the `tabbable` dependency is *not* bundled with `focus-trap`. It must be included separately and loaded *before* `focus-trap`.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Ensure the DOM element passed to `createFocusTrap` exists and is a valid `HTMLElement`. Check for `null` or `undefined` returns from `document.getElementById` or `document.querySelector`.","cause":"`createFocusTrap` was called with a non-existent or null DOM element, resulting in `undefined` being returned. Subsequently, `undefined.activate()` throws an error.","error":"TypeError: Cannot read properties of undefined (reading 'activate')"},{"fix":"For Safari, ensure the specific setting is enabled. Verify that your elements are natively tabbable or correctly marked with `tabindex`. Review the `tabbable` library documentation for details on what elements are considered tabbable. Also, check for CSS properties like `display: none` or `visibility: hidden` on the trap's container or its tabbable children.","cause":"This is often due to the Safari 'Press Tab to highlight each item on a webpage' setting being disabled, or incorrectly configured `initialFocus` options, or elements within the trap not being correctly identified as tabbable by `tabbable`.","error":"Focus trap is not working as expected (e.g., focus escapes the modal, tabbing order is incorrect, clicks outside are not blocked)."},{"fix":"Ensure the UMD script for `focus-trap` is properly loaded. When loaded via UMD, the exports are typically available under `window.FocusTrap`, so you would access `window.FocusTrap.createFocusTrap`.","cause":"Occurs when using the UMD build (e.g., from unpkg) but trying to access `FocusTrap` globally without it being loaded, or trying to destructure `createFocusTrap` when only `window.FocusTrap` is available.","error":"ReferenceError: FocusTrap is not defined"}],"ecosystem":"npm"}