0Pricing
React Academy · Lesson

Handling Focus & Keyboard Traps in Modals

Keep focus inside open modals for accessibility using useRef and event listeners.

Handling Focus & Keyboard Traps in Modals is a free React Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Welcome

In this lesson you will implement a focus trap inside a modal so that keyboard users cannot Tab their way out of an open modal — a key accessibility requirement (WCAG 2.4.3).

Why Focus Traps Are Required

When a modal opens, keyboard users should be able to interact only with the modal's content. Without a focus trap, pressing Tab moves focus to elements behind the modal overlay — those elements are visually hidden but still reachable.

What Is a Focus Trap?

A focus trap intercepts Tab and Shift+Tab key events and, when focus would leave the modal, wraps it back to the first or last focusable element inside the modal.

Finding Focusable Elements

Use querySelectorAll with a selector that covers all interactive elements. Filter out disabled and hidden ones.
const FOCUSABLE = [
  'a[href]', 'button:not([disabled])',
  'input:not([disabled])', 'select', 'textarea',
  '[tabindex]:not([tabindex="-1"])'
].join(',');

const focusable = Array.from(
  modalRef.current.querySelectorAll(FOCUSABLE)
);

Implementing the Trap

Listen for the keydown event on the modal container. On Tab, if focus is on the last element, move it to the first. On Shift+Tab, if focus is on the first, move it to the last.
function handleTab(e) {
  const focusable = /* array of focusable elements */;
  const first = focusable[0];
  const last = focusable[focusable.length - 1];
  if (e.key !== 'Tab') return;
  if (e.shiftKey) {
    if (document.activeElement === first) {
      e.preventDefault(); last.focus();
    }
  } else {
    if (document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  }
}

Moving Focus on Open

When the modal opens, move focus to the first focusable element inside it (or to the modal container itself with `tabIndex=-1`). This prevents focus from staying on the button that opened the modal.
useEffect(() => {
  if (isOpen) {
    const first = modalRef.current?.querySelector(FOCUSABLE);
    first?.focus();
  }
}, [isOpen]);

Restoring Focus on Close

When the modal closes, return focus to the element that was active before the modal opened. Store a ref to `document.activeElement` at the time the modal opens.
const previousFocus = useRef(null);

useEffect(() => {
  if (isOpen) {
    previousFocus.current = document.activeElement;
  } else {
    previousFocus.current?.focus();
  }
}, [isOpen]);

Using the focus-trap-react Library

For production use, consider the **focus-trap-react** library. It handles all edge cases: shadow DOM, iframes, dynamically added focusable elements, and deactivation on Escape.
import FocusTrap from 'focus-trap-react';

{isOpen && (
  <FocusTrap>
    <div role="dialog" aria-modal="true">
      {children}
    </div>
  </FocusTrap>
)}

ARIA Attributes for Modals

Add `role="dialog"` and `aria-modal="true"` to the modal container. Add `aria-labelledby` pointing to the modal's heading. This tells screen readers that the dialog is a modal and announces it correctly.
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">Confirm Delete</h2>
  ...
</div>

Inert Attribute for Background

The HTML `inert` attribute on a container removes all its elements from focus order and screen reader accessibility. Set `document.getElementById('root').inert = true` while the modal is open for a robust background block.
useEffect(() => {
  const root = document.getElementById('root');
  if (isOpen) root.setAttribute('inert', '');
  else root.removeAttribute('inert');
}, [isOpen]);

Quick Check

What should happen when a keyboard user presses Tab on the last focusable element inside a focus-trapped modal?

Recap

You implemented a focus trap: find focusable elements, intercept Tab/Shift+Tab, move focus on open, restore focus on close. Use focus-trap-react for production and add role='dialog' and aria-modal for screen readers.

Up Next

Next lesson: **Tooltips & Dropdown Menus with Portals** — you will render tooltips and dropdowns that escape overflow-hidden containers using createPortal.

Frequently asked questions

Is the “Handling Focus & Keyboard Traps in Modals” lesson free?

Yes — the full text of “Handling Focus & Keyboard Traps in Modals” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.

What will I learn in “Handling Focus & Keyboard Traps in Modals”?

Keep focus inside open modals for accessibility using useRef and event listeners. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start React Academy?

No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Handling Focus & Keyboard Traps in Modals” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this React Academy lesson?

Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. What Are React Portals?
  2. Building a Modal Component with Portals
  3. Handling Focus & Keyboard Traps in Modals
  4. Tooltips & Dropdown Menus with Portals
← Back to React Academy