useHotkeys

Supports key combinations
Supports assigning hotkeys to a specific page element
Groups all hotkeys under a single event listener


import { useHotkeys } from "reshaped";

useHotkeys triggers callbacks passed to it when a hotkey is pressed. It accepts an object with hotkeys defined as object keys and callbacks as object values.

You can pass a single hotkey or multiple hotkeys split by comma, which will trigger a callback when any of them is pressed. Key codes passed to the hook should match the event.key values.

// Single hotkey
useHotkeys({ n: createNewTask });

// An array of hotkeys for the same callback
useHotkey({ "n, t": createNewTask });

useHotkeys can be used for combinations of keys pressed together, if you separate them with a + sign. Spacing and letter casing should matter in this case, since useHotkey automatically formats all the values passed to it.

useHotkeys({ "Shift + b": switchView });

For cross-platform hotkeys, you can use a special mod key which will trigger for both cmd and ctrl keyboard keys.

useHotkeys({ "mod + b": switchView });

In case you want to prevent the default behavior of the pressed keys, you can use the event argument provided by the callback. This also mean you can prevent the default behavior only when a certain condition is met and not every time hotkey is pressed.

useHotkeys({
  n: (event) => {
    event.preventDefault();
  },
});

By default useHotkeys uses a window event handler, which means callback gets triggered anytime user pressed the hotkey. If you want your action to be triggered only for a specific element or an area of the page, you can use ref returned from the hook.

Note that a single ref for all hotkeys passed to it, which means you can assign all of them to the same element. In case you want to assign them to different elements, you can use multiple useHotkeys calls.

function Example() {
  const [count, setCount] = React.useState(0);
  const value = count > 0 ? `Value ${count}` : "";
  const { ref } = useHotkeys({
    ArrowUp: () => setCount((prev) => prev + 1),
    ArrowDown: () => setCount((prev) => Math.max(0, prev - 1)),
  });

  return (
    <TextField
      inputAttributes={{ ref }}
      placeholder="Use up and down arrow keys"
      value={value}
    />
  );
}

When using with TypeScript, it is likely will ask to provide a more specific type for the DOM element you're using it with. Same as in other React hooks, you can pass that element type with a generic:

const { ref } = useHotkeys<HTMLInputElement>({ "Shift + Enter": submitForm });

If you already have a ref for the element and don't want useHotkeys to create a new one – you can pass your own ref to the useHotkeys options.

const { ref } = useHotkeys(
  {
    ArrowUp: () => setCount((prev) => prev + 1),
  },
  [], // dependences array, mentioned below
  { ref: myRef },
);

Additionally useHotkeys returns a checkHotkeyState function which you can use to checked if any hotkey is currently pressed. You can then use its returned boolean value to conditionally render content based on the state. It's possible to use this function result without passing a callback for the hotkey.

const { checkHotkeyState } = useHotkeys({ "Shift + b": null });

// User pressed Shift + b

checkHotkeyState("Shift + b"); // true
checkHotkeyState("c"); // false

Since all callbacks are binded with useEffect, they might also get cached for the further calls. Same as in effects, useHotkeys supports a dependency array:

const { count } = props;

useHotkeys(
  {
    b: () => console.log(count),
  },
  [count],
);

If you want to attach the keyboard events to a ref and you already have a ref created in your component, you can pass it as an option:

const { ref } = useHotkeys({...}, [], { ref: inputRef });

You can temporary turn off all hotkeys with a disabled option. This can be useful when your component has an inactive or a disabled state and you don't want to handle it separately in each hotkey handler.

useHotkeys({...}, [], { disabled: !isActive });

Some keys are reserved in the browsers by default. For example, pressing up and down arrow keys scrolls the webpage. If you want to prevent that default behavior for all hotkeys defined in the hook, use the preventDefault option.

useHotkeys({...}, [], { preventDefault: true });
(
  hotkeys: Record<string, (event) => void | null>,
  deps: unknown[],
  options: { ref: React.Ref<HTMLElement>, disabled?: boolean, preventDefault?: boolean }
) => {
  ref: React.RefObject<HTMLElement>,
	checkHotkeyState: (hotkey: string) => boolean
}