8
Hooks

useKeyboard

A hook that handles keyboard events with a simple key-to-handler mapping.

useKeyboard

The useKeyboard hook provides a simple and declarative way to handle keyboard events in React components. Instead of manually setting up event listeners, you can define a mapping of keys to handler functions, making your keyboard interaction code more readable and maintainable.

Keyboard Demo

Press arrow keys, space, or enter

Waiting for keyboard input...

Installation

Install the useKeyboard hook using:

npx axionjs-ui add hook use-keyboard

File Structure

use-keyboard.ts

Parameters

PropTypeDefault
handlers
Record<string, (event: KeyboardEvent) => void>
Required
options
Object
{}

Options

PropTypeDefault
target
Window | HTMLElement | null
window
event
'keydown' | 'keyup' | 'keypress'
'keydown'
enabled
boolean
true

Examples

Game Controls

Implement keyboard controls for a simple game:

Game Controls Demo

Use arrow keys to move the square

Use +/- keys to resize

Create a modal that can be closed with the Escape key:

import { useState } from "react";
import { useKeyboard } from "@/hooks/use-keyboard";
 
function KeyboardModal() {
  const [isOpen, setIsOpen] = useState(false);
  
  // Only enable keyboard handling when modal is open
  useKeyboard(
    {
      escape: () => setIsOpen(false),
      enter: (e) => {
        e.preventDefault();
        // Handle confirm action
        console.log("Confirmed!");
        setIsOpen(false);
      },
    },
    { enabled: isOpen }
  );
  
  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      
      {isOpen && (
        <div className="modal">
          <div className="modal-content">
            <h2>Keyboard Navigation</h2>
            <p>Press ESC to close or ENTER to confirm</p>
            <button onClick={() => setIsOpen(false)}>Close</button>
          </div>
        </div>
      )}
    </div>
  );
}

Form Keyboard Shortcuts

Add keyboard shortcuts to a form:

import { useRef } from "react";
import { useKeyboard } from "@/hooks/use-keyboard";
 
function ShortcutForm() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);
  const messageRef = useRef(null);
  const submitRef = useRef(null);
  
  useKeyboard({
    // Alt+1, Alt+2, etc. to focus different fields
    "1": (e) => {
      if (e.altKey) {
        e.preventDefault();
        nameRef.current?.focus();
      }
    },
    "2": (e) => {
      if (e.altKey) {
        e.preventDefault();
        emailRef.current?.focus();
      }
    },
    "3": (e) => {
      if (e.altKey) {
        e.preventDefault();
        messageRef.current?.focus();
      }
    },
    "s": (e) => {
      // Ctrl+S to submit
      if (e.ctrlKey) {
        e.preventDefault();
        submitRef.current?.click();
      }
    },
  });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    // Submit form logic
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Name (Alt+1)</label>
        <input ref={nameRef} type="text" />
      </div>
      
      <div>
        <label>Email (Alt+2)</label>
        <input ref={emailRef} type="email" />
      </div>
      
      <div>
        <label>Message (Alt+3)</label>
        <textarea ref={messageRef}></textarea>
      </div>
      
      <button ref={submitRef} type="submit">
        Submit (Ctrl+S)
      </button>
    </form>
  );
}

Supported Keys

This hook works with standard key names as provided by the event.key property in the browser. Here are some commonly used keys:

  • Arrow keys: arrowup, arrowdown, arrowleft, arrowright
  • Special keys: enter, tab, escape (or esc), space, backspace
  • Letter keys: a, b, c, etc.
  • Number keys: 0, 1, 2, etc.
  • Symbol keys: +, -, /, etc.

For a complete list of key values, refer to the MDN KeyboardEvent key reference.

Modifier Keys

The hook passes the full KeyboardEvent to your handlers, so you can check for modifier keys:

useKeyboard({
  s: (e) => {
    if (e.ctrlKey) {
      e.preventDefault();
      console.log("Ctrl+S pressed");
      // Save action
    }
  },
  p: (e) => {
    if (e.ctrlKey && e.shiftKey) {
      e.preventDefault();
      console.log("Ctrl+Shift+P pressed");
      // Print action
    }
  },
});

Use Cases

  • Keyboard Shortcuts: Implement application-wide or component-specific shortcuts
  • Games: Handle directional controls for games
  • Accessibility: Enhance keyboard navigation
  • Modal/Dialog Controls: Close with Escape or confirm with Enter
  • Form Navigation: Move between fields with keyboard
  • Media Controls: Play/pause/skip with keyboard
  • Slideshows: Navigate between slides with arrow keys
  • Command Palettes: Trigger command interfaces with keyboard
  • Drawing Applications: Tool selection with keyboard
  • Text Editors: Key bindings for editing actions

Multiple Key Combinations

For detecting multiple keys pressed simultaneously (beyond just modifier keys), you can track key state:

function KeyComboDemo() {
  const [keysPressed, setKeysPressed] = useState(new Set());
  
  const checkCombo = useCallback(() => {
    if (keysPressed.has("control") && keysPressed.has("shift") && keysPressed.has("a")) {
      console.log("Ctrl+Shift+A detected!");
    }
  }, [keysPressed]);
  
  useKeyboard({
    // Track key down
    control: (e) => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.add("control");
        return next;
      });
      checkCombo();
    },
    shift: (e) => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.add("shift");
        return next;
      });
      checkCombo();
    },
    a: (e) => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.add("a");
        return next;
      });
      checkCombo();
    },
  }, { event: 'keydown' });
  
  // Clear keys on key up
  useKeyboard({
    control: () => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.delete("control");
        return next;
      });
    },
    shift: () => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.delete("shift");
        return next;
      });
    },
    a: () => {
      setKeysPressed(prev => {
        const next = new Set(prev);
        next.delete("a");
        return next;
      });
    },
  }, { event: 'keyup' });
  
  return (
    <div>
      <h2>Try pressing: Ctrl+Shift+A</h2>
      <div>
        Currently pressed: {Array.from(keysPressed).join(", ")}
      </div>
    </div>
  );
}

Accessibility

When implementing keyboard shortcuts, consider these accessibility guidelines:

  • Use common keyboard patterns that match user expectations
  • Provide visible indicators for available keyboard shortcuts
  • Avoid overriding browser or screen reader shortcuts
  • Make sure all functionality is also available without keyboard shortcuts
  • Test with screen readers and keyboard-only navigation
  • Consider adding a keyboard shortcuts help section

Browser Compatibility

The hook uses the standard KeyboardEvent API which is well-supported across modern browsers. The event.key property (used for key identification) is supported in all major browsers.

Performance Considerations

  • Consider performance implications when handling fast key presses (e.g., in games)
  • For complex key handling, such as in games, you might want to implement your own key state tracking for better performance

Best Practices

  • Use preventDefault() when implementing shortcuts to avoid triggering browser defaults
  • Focus on the target element when using the hook with specific DOM elements
  • Consider conflict management when implementing global shortcuts
  • Document keyboard shortcuts for users
  • Group related shortcuts in a consistent manner
  • Consider keyboard layout differences for international users
  • Use descriptive names for handler functions
  • Disable handlers when components are inactive to prevent interference

On this page