npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usefy/use-key-press

v0.25.1

Published

A React hook for detecting keyboard key presses, shortcuts, and combinations

Readme


Overview

@usefy/use-key-press is a feature-rich React hook for detecting keyboard input — from a single key to complex modifier combinations. It supports cross-platform shortcuts, alternative bindings, physical-key matching, and callbacks with the raw event, making it ideal for command palettes, editor shortcuts, and game controls.

Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.

Why use-key-press?

  • Zero Dependencies — Pure React implementation with no external dependencies
  • TypeScript First — Full type safety with comprehensive type definitions
  • Shortcuts & Combinations"Escape", "ctrl+s", "mod+shift+k" out of the box
  • Alternative Bindings — Arrays are matched as OR: ["ctrl+s", "meta+s"]
  • Cross-Platform mod — Resolves to Ctrl on Windows/Linux, Cmd on macOS
  • Key or Code Matching — Layout-aware (event.key) or physical (event.code) for WASD-style controls
  • CallbacksonPress / onRelease receive the raw event for preventDefault
  • Robust — Ignores auto-repeat & typing in inputs (opt-in), resets on window blur
  • SSR Compatible — Safe listener setup and automatic cleanup

Installation

# npm
npm install @usefy/use-key-press

# yarn
yarn add @usefy/use-key-press

# pnpm
pnpm add @usefy/use-key-press

Peer Dependencies

This package requires React 18 or 19:

{
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0"
  }
}

Quick Start

import { useKeyPress } from "@usefy/use-key-press";

function Modal({ onClose }: { onClose: () => void }) {
  const escapePressed = useKeyPress("Escape");

  useEffect(() => {
    if (escapePressed) onClose();
  }, [escapePressed, onClose]);

  return <div>Press Escape to close</div>;
}

Shortcut with a callback

// mod = Ctrl on Windows/Linux, Cmd on macOS
useKeyPress("mod+k", {
  preventDefault: true,
  onPress: () => openCommandPalette(),
});

API Reference

useKeyPress(target, options?)

Detects whether target is currently pressed and returns a boolean.

Parameters

| Parameter | Type | Description | | --------- | -------------------- | -------------------------------------------------- | | target | KeyPressTarget | The key(s) or predicate to detect (see below) | | options | UseKeyPressOptions | Optional configuration object |

targetKeyPressTarget

| Form | Example | Meaning | | ------------------ | ------------------------------ | --------------------------------------------------------- | | string | "Escape", "ctrl+s" | A single key or a +-joined modifier combination | | string[] | ["ctrl+s", "meta+s"] | Alternative bindings — matches if any applies (OR) | | predicate function | (e) => /^[0-9]$/.test(e.key) | Full control over matching |

Modifier aliases: ctrl/control, shift, alt/option/opt, meta/cmd/command/win, and mod (Ctrl on Windows/Linux, Cmd on macOS).

Key aliases: esc/escape, space, up/down/left/right (arrows), enter/return, del/delete, tab, backspace, plus, and more.

Options — UseKeyPressOptions

| Option | Type | Default | Description | | --------------------- | --------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------- | | target | Window \| Document \| HTMLElement \| RefObject \| null | window | The element to attach listeners to. null detaches | | eventType | "keydown" \| "keyup" \| "both" | "both" | Which events drive the pressed state. "both" tracks the full held lifecycle | | enabled | boolean | true | When false, no listeners are attached and the result is always false | | preventDefault | boolean | false | Call event.preventDefault() on a match (e.g. override the browser's ctrl+s) | | stopPropagation | boolean | false | Call event.stopPropagation() on a match | | ignoreRepeat | boolean | true | Ignore auto-repeated keydown events for onPress (does not affect the returned boolean) | | ignoreInputElements | boolean | false | Ignore events from <input>, <textarea>, <select>, and contenteditable elements | | caseSensitive | boolean | false | Match letters case-sensitively (key mode only) | | matchBy | "key" \| "code" | "key" | Match the logical key (event.key) or the physical key (event.code) | | exactModifiers | boolean | true | Require an exact modifier match. When true, "ctrl+s" does not match ctrl+shift+s | | onPress | (event: KeyboardEvent) => void | — | Called on a matching keydown, with the raw event | | onRelease | (event: KeyboardEvent) => void | — | Called when a matched key is released |

Returns

booleantrue while the target key/combination is pressed (with the default eventType: "both").


Examples

Cross-platform save shortcut

import { useKeyPress } from "@usefy/use-key-press";

function Editor({ onSave }: { onSave: () => void }) {
  // Array = OR: matches Ctrl+S on Windows/Linux and Cmd+S on macOS
  useKeyPress(["ctrl+s", "meta+s"], {
    preventDefault: true, // stop the browser's Save dialog
    onPress: () => onSave(),
  });

  return <textarea />;
}

Command palette

import { useState } from "react";
import { useKeyPress } from "@usefy/use-key-press";

function App() {
  const [open, setOpen] = useState(false);

  useKeyPress("mod+k", {
    preventDefault: true,
    onPress: () => setOpen((prev) => !prev),
  });

  return open ? <CommandPalette /> : null;
}

WASD game controls (physical keys)

import { useKeyPress } from "@usefy/use-key-press";

function Game() {
  // matchBy: "code" is layout-independent (works on AZERTY, Dvorak, etc.)
  const up = useKeyPress("w", { matchBy: "code" });
  const left = useKeyPress("a", { matchBy: "code" });
  const down = useKeyPress("s", { matchBy: "code" });
  const right = useKeyPress("d", { matchBy: "code" });

  return <Player up={up} left={left} down={down} right={right} />;
}

Ignore shortcuts while typing

import { useKeyPress } from "@usefy/use-key-press";

function App() {
  // "f" opens a filter — but not while the user types "f" in a field
  useKeyPress("f", {
    ignoreInputElements: true,
    onPress: () => openFilter(),
  });

  return <input placeholder="Type freely..." />;
}

Custom predicate

import { useKeyPress } from "@usefy/use-key-press";

function NumericField() {
  const digitPressed = useKeyPress((e) => /^[0-9]$/.test(e.key));
  return <div data-active={digitPressed}>Press any number</div>;
}

Scoped to a specific element

import { useRef } from "react";
import { useKeyPress } from "@usefy/use-key-press";

function ScopedInput() {
  const ref = useRef<HTMLInputElement>(null);

  // Only reacts while the input is the event target
  const enterPressed = useKeyPress("Enter", { target: ref });

  return <input ref={ref} data-submit={enterPressed} />;
}

TypeScript

This hook is written in TypeScript and exports comprehensive type definitions.

import {
  useKeyPress,
  type KeyPressTarget,
  type UseKeyPressOptions,
  type KeyPressEventType,
  type KeyPressMatchBy,
  type ParsedShortcut,
} from "@usefy/use-key-press";

const options: UseKeyPressOptions = {
  preventDefault: true,
  matchBy: "key",
  onPress: (event) => console.log(event.key),
};

const isPressed: boolean = useKeyPress("mod+shift+p", options);

Utility helpers such as parseShortcut, isKeyPressSupported, and isApplePlatform are also exported for advanced use.


Behavior Notes

  • Held state — With the default eventType: "both", the returned boolean is true only while the key/combination is physically held.
  • Stuck-key prevention — If focus leaves the window while a key is held (so keyup is never delivered), the state resets on blur. To keep onPress/onRelease balanced, onRelease also fires once on that blur, receiving a synthetic keyup event. onRelease is not fired when the hook is disabled (enabled: false) or unmounted while pressed.
  • Modifier release — For combinations, releasing either the primary key or a modifier that was part of the held trigger ends the pressed state. Releasing an unrelated modifier (e.g. Shift while holding a bare "a") does not reset it.
  • SSR — In non-browser environments the hook safely returns false and attaches no listeners.

Browser Support

This hook uses standard keydown/keyup events, supported in all modern browsers:

  • Chrome 1+
  • Firefox 1+
  • Safari 1+
  • Edge 12+
  • Opera 7+

For SSR environments, the hook gracefully degrades and returns false.


Testing

This package maintains comprehensive test coverage to ensure reliability and stability.

Test Coverage

📊 View Detailed Coverage Report (GitHub Pages)

Test Files

  • useKeyPress.test.ts — 60 tests for hook behavior and utilities

Total: 60 tests


License

MIT © mirunamu

This package is part of the usefy monorepo.