Skip to content

Keyboard Events React

DodaTech 3 min read

title: "Keyboard Events in React" weight: 15 description: "Learn how to handle keyboard events in React components for accessibility: onKeyDown for arrow navigation, Enter and Space for activation, Escape for dismissal, and proper keyboard event patterns for custom widgets." date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, react]


Keyboard events in React require onKeyDown handlers for custom interactive components, supporting arrow key navigation for lists and menus, Enter and Space for activation, Escape for dismissal, and Home/End for boundary navigation.

## What You'll Learn

You will implement keyboard event handlers in React, follow ARIA keyboard patterns, handle focus movement with arrow keys, and support standard keyboard interactions for custom widgets.

## Why It Matters

Custom interactive components -- dropdowns, lists, sliders, tree views -- require explicit keyboard handling. React's event system makes this straightforward, but the patterns must follow platform conventions that users expect.

## Real-World Use

A listbox component allows arrow key navigation within the list, Enter to select an item, and Escape to close. Without these handlers, keyboard users cannot interact with the listbox at all.

## Keyboard Pattern Flow

```mermaid
flowchart TD
  A[Keyboard Event] --> B{Which Key?}
  B -->|ArrowUp/Down| C[Move focus in list]
  B -->|Enter/Space| D[Activate/Select]
  B -->|Escape| E[Dismiss/Close]
  B -->|Home/End| F[First/Last item]
  B -->|Tab| G[Next focusable element]

Implementing Keyboard Events

Follow ARIA authoring practices for keyboard patterns.

function Listbox({ items, selected, onSelect }) {
  const listRef = useRef(null);
  const [activeIndex, setActiveIndex] = useState(0);

  const handleKeyDown = (e) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setActiveIndex(prev => Math.min(prev + 1, items.length - 1));
        break;
      case 'ArrowUp':
        e.preventDefault();
        setActiveIndex(prev => Math.max(prev - 1, 0));
        break;
      case 'Home':
        e.preventDefault();
        setActiveIndex(0);
        break;
      case 'End':
        e.preventDefault();
        setActiveIndex(items.length - 1);
        break;
      case 'Enter':
      case ' ':
        e.preventDefault();
        onSelect(items[activeIndex]);
        break;
    }
  };

  return (
    <ul
      ref={listRef}
      role="listbox"
      tabIndex={0}
      onKeyDown={handleKeyDown}
      aria-activedescendant={`option-${activeIndex}`}
    >
      {items.map((item, index) => (
        <li
          key={index}
          id={`option-${index}`}
          role="option"
          aria-selected={selected === item}
          className={activeIndex === index ? 'active' : ''}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}
// Slider with keyboard support
function Slider({ value, onChange, min = 0, max = 100 }) {
  const handleKeyDown = (e) => {
    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowUp':
        e.preventDefault();
        onChange(Math.min(value + 1, max));
        break;
      case 'ArrowLeft':
      case 'ArrowDown':
        e.preventDefault();
        onChange(Math.max(value - 1, min));
        break;
      case 'Home':
        e.preventDefault();
        onChange(min);
        break;
      case 'End':
        e.preventDefault();
        onChange(max);
        break;
    }
  };

  return (
    <div
      role="slider"
      tabIndex={0}
      aria-valuemin={min}
      aria-valuemax={max}
      aria-valuenow={value}
      onKeyDown={handleKeyDown}
    >
      {value}
    </div>
  );
}

Common Mistakes

  • Only handling onClick without keyboard alternatives
  • Using onKeyPress instead of onKeyDown (onKeyPress is deprecated)
  • Preventing default on Tab key (breaks navigation)
  • Not managing focus movement with arrow keys
  • Forgetting to add role and aria-* attributes alongside keyboard handlers
  • Not supporting Home and End keys for list navigation
  • Implementing keyboard handling without visual feedback

Practice and Challenge

Practice 1: Add arrow key navigation to a list component. Practice 2: Implement Escape key handling for a modal. Practice 3: Create a slider with keyboard support. Practice 4: Add keyboard handling to a menu button. Practice 5: Ensure Tab works correctly for leaving custom widgets.

Challenge: Build a complete keyboard-navigable widget in React: a combination of a search input with autocomplete suggestions. The autocomplete dropdown should support arrow up/down to navigate suggestions, Enter to select, Escape to close, and Tab to select and move to next field.

FAQ

Which keyboard event should I use?

Use onKeyDown for all keyboard handling. It fires consistently across browsers.

Should I handle onKeyPress?

onKeyPress is deprecated. Use onKeyDown instead.

How do I prevent scrolling with arrow keys?

Call e.preventDefault() on arrow key events within the component.

Do I need keyboard handlers on semantic elements?

Semantic button and a elements handle Enter and Space automatically. Custom widgets need explicit handlers.

What about keyboard shortcuts?

Use aria-keyshortcuts to inform screen readers of keyboard shortcuts. Avoid overriding browser shortcuts.

How do I test keyboard interactions?

Use keyboard-only testing: Tab, Shift+Tab, Enter, Space, Escape, and arrow keys.

Mini Project

Create a React keyboard navigation demo with four interactive widgets: listbox (arrow keys + Enter), slider (arrow keys + Home/End), menu button (Enter opens, arrows navigate, Escape closes), and modal (Escape closes, focus trap). Each widget should follow ARIA authoring practices.

What's Next

Form Accessibility in React covers accessible form patterns in React.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro