Fiber UI LogoFiberUI

useKeyPress

A React hook that detects when a specific key is pressed.

Installation

npx shadcn@latest add https://r.fiberui.com/r/hooks/use-key-press.json

Features

  • Specific Key Detection - Detects press state of any key by name.
  • Real-time Updates - Updates state on keydown and keyup.
  • Global Listener - Listens to window events, so it works anywhere on the page.

Source Code

View the full hook implementation in the Hook Source Code section below.

Interactive Example

Key Press Detection

Press the following keys to see their state:

EnterReleased
EscapeReleased
SpaceReleased
ShiftReleased
"use client";

import { useKeyPress } from "@repo/hooks/dom/use-key-press";
import { Keyboard } from "lucide-react";

export const Example1 = () => {
    // Track specific keys
    const isEnterPressed = useKeyPress("Enter");
    const isEscapePressed = useKeyPress("Escape");
    const isSpacePressed = useKeyPress(" ");
    const isShiftPressed = useKeyPress("Shift");

    return (
        <div className="flex w-full max-w-md flex-col gap-4">
            <div className="flex items-center gap-2">
                <Keyboard className="text-primary h-5 w-5" />
                <h3 className="font-semibold">Key Press Detection</h3>
            </div>

            <p className="text-muted-foreground text-sm">
                Press the following keys to see their state:
            </p>

            <div className="grid grid-cols-2 gap-3">
                <KeyDisplay label="Enter" isPressed={isEnterPressed} />
                <KeyDisplay label="Escape" isPressed={isEscapePressed} />
                <KeyDisplay label="Space" isPressed={isSpacePressed} />
                <KeyDisplay label="Shift" isPressed={isShiftPressed} />
            </div>
        </div>
    );
};

const KeyDisplay = ({
    label,
    isPressed,
}: {
    label: string;
    isPressed: boolean;
}) => (
    <div
        className={`flex items-center justify-between rounded-lg border px-3 py-2 text-sm transition-all duration-150 ${
            isPressed
                ? "border-primary bg-primary/10 text-primary scale-105 font-medium shadow-sm"
                : "border-muted bg-muted/20 text-muted-foreground"
        }`}
    >
        <span>{label}</span>
        <span className="text-xs">{isPressed ? "Pressed" : "Released"}</span>
    </div>
);
const isEnterPressed = useKeyPress("Enter");
const isEscapePressed = useKeyPress("Escape");

if (isEnterPressed) {
    console.log("Enter key is down!");
}

Game Controls

Detects multiple keys for movement (WASD or Arrows).

Game Controls

W / ↑
A / ←
S / ↓
D / →

Character is idle.

"use client";

import { useKeyPress } from "@repo/hooks/dom/use-key-press";
import { Gamepad2 } from "lucide-react";

export const Example2 = () => {
    const up = useKeyPress("ArrowUp");
    const down = useKeyPress("ArrowDown");
    const left = useKeyPress("ArrowLeft");
    const right = useKeyPress("ArrowRight");
    const w = useKeyPress("w");
    const a = useKeyPress("a");
    const s = useKeyPress("s");
    const d = useKeyPress("d");

    const isMoving = up || down || left || right || w || a || s || d;

    return (
        <div className="flex w-full max-w-md flex-col items-center gap-6">
            <div className="flex items-center gap-2">
                <Gamepad2
                    className={`h-5 w-5 ${isMoving ? "text-primary animate-pulse" : ""}`}
                />
                <h3 className="font-semibold">Game Controls</h3>
            </div>

            <div className="grid grid-cols-3 gap-2">
                <div />
                <KeyCap label="W / ↑" active={up || w} />
                <div />
                <KeyCap label="A / ←" active={left || a} />
                <KeyCap label="S / ↓" active={down || s} />
                <KeyCap label="D / →" active={right || d} />
            </div>

            <p className="text-muted-foreground text-center text-sm">
                {isMoving ? "Character is moving!" : "Character is idle."}
            </p>
        </div>
    );
};

const KeyCap = ({ label, active }: { label: string; active: boolean }) => (
    <div
        className={`flex h-12 w-16 items-center justify-center rounded-lg border-b-4 text-sm font-bold transition-all ${
            active
                ? "border-primary bg-primary text-primary-foreground translate-y-1 border-b-0"
                : "border-muted-foreground/30 bg-muted"
        }`}
    >
        {label}
    </div>
);
const up = useKeyPress("ArrowUp"); 
const w = useKeyPress("w"); 
const isMovingUp = up || w; 

API Reference

Signature

const isPressed = useKeyPress(targetKey);

Parameters

ParameterTypeDescription
targetKeystringThe key to listen for (e.g., "Enter", "Escape", "a", "Shift"). Matches event.key.

Returns

TypeDescription
booleantrue if the key is currently pressed, false otherwise.

Hook Source Code

import { useState, useCallback, useRef, useEffect } from "react";
import {
    useEventListener,
    EventListenerOptions,
} from "@repo/hooks/dom/use-event-listener";

/**
 * useKeyPress - Detect when a specific key is pressed
 *
 * @param targetKey - The key to listen for (e.g., "Escape", "Enter", "a")
 * @param callback - Optional callback when key is pressed
 * @param options - Optional event listener options
 * @returns boolean indicating if the key is currently pressed
 *
 * @example
 * // Using the boolean state
 * const isEscapePressed = useKeyPress("Escape");
 *
 * // Using the callback
 * useKeyPress("Enter", () => submitForm());
 *
 * // With options
 * useKeyPress("Tab", () => handleTab(), { preventDefault: true });
 */
export function useKeyPress(
    targetKey: string,
    callback?: (event: KeyboardEvent) => void,
    options?: EventListenerOptions,
): boolean {
    const [isPressed, setIsPressed] = useState(false);
    const callbackRef = useRef(callback);

    useEffect(() => {
        callbackRef.current = callback;
    }, [callback]);

    const handleKeyDown = useCallback(
        (event: KeyboardEvent) => {
            if (event.key.localeCompare(targetKey) === 0) {
                setIsPressed(true);
                callbackRef.current?.(event);
            }
        },
        [targetKey],
    );

    const handleKeyUp = useCallback(
        (event: KeyboardEvent) => {
            if (event.key.localeCompare(targetKey) === 0) {
                setIsPressed(false);
            }
        },
        [targetKey],
    );

    useEventListener("keydown", handleKeyDown, null, options);
    useEventListener("keyup", handleKeyUp);

    return isPressed;
}