Skip to main content
Version: 3.x

useHotkeys API

Function signature:

function useHotkeys<T extends Element>(
keys: string,
callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
options: Options = {},
deps: any[] = []
): React.MutableRef<T | null>

Arguments

keys

keys: string

Set the keystrokes we want the hook to listen to. We can use single or multiple keys, modifier combinations, etc. See this guide of the underlying hotkeys package for all possible shortcuts.

Listening to all keys

useHotkeys('*', (_, handler) => alert(handler.key))

Using modifiers

useHotkeys('ctrl+s, shift+w', () => alert('We\'re using modifiers now!'))
Using the alt modifier

The alt modifier has its own special handling. To check if an altKey was pressed we need to listen to all keys and check for the alt modifier in the event object:

useHotkeys('*', e => {
console.log('Pressed alt modifier', e.altKey);
console.log('Pressed key', e.key)
})

This will change in the upcoming v4.

Using F keys

useHotkeys('f5', () => alert('F5 was pressed'))

Using multiple keys

useHotkeys('w, a, s, d', () => alert('Player moved!'))
Differentiating between multiple possible keys

If we use a combination of possible keys that use the same hook, we can use handler.key to check which key the user pressed.

useHotkeys('ctrl+a, shift+b, r, f', (_, handler) => {
switch (handler.key) {
case 'ctrl+a': alert('You pressed ctrl+a!');
break;
case 'ctrl+b': alert('You pressed ctrl+b!');
break;
case 'r': alert('You pressed r!');
break;
case 'f': alert('You pressed f!');
break;
}
})

callback

callback: (event: KeyboardEvent, handler: HotkeysEvent) => void

Gets executed when the defined keystroke gets hit by the user. event holds the browsers keyboard event, handler passes some additional information to handle the pressed key.

event

The browsers native keyboard event that gets created when the user hits a key. For a thorough documentation of this event check out the MDN Web Docs.

handler

The handler holds information about the pressed key. In general, we should only need this object to handle our keyboard events. The most important property of the handler object is the key prop:

  • key: string - This will hold the pressed keystroke. So if we use multiple possible keystroke combinations for the same callback we can use this property to check which specific keystroke was pressed.

There are more properties attached to the handle that currently don't get populated with values. So we can safely ignore those.

The callback gets memoised

The callback we pass into the hook gets memoised, so every variable we reference inside the callback must be added to the dependencies array, otherwise we will get stale values. For more on memoisation in the context of React hooks read this nice article.


options

We can extensively configure how the hook behaves by passing it an options object. Below are all properties that the object takes.

// Default values
const options = {
filter: undefined,
filterPreventDefault: true,
enableOnTags: [],
enabled: true,
splitKey: '+',
keyup: undefined,
keydown: true
};
// Type Definitions
type AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';

type Options = {
enabled?: boolean;
filter?: (event: KeyboardEvent) => boolean;
filterPreventDefault?: boolean;
enableOnTags?: AvailableTags[];
enableOnContentEditable?: boolean;
splitKey?: string;
keyup?: boolean;
keydown?: boolean;
};

Properties

filter
filter: (event: KeyboardEvent) => boolean // default: undefined

Pass a function filter to determine if the callback should get triggered. Return false to prevent the execution of the callback and true to allow the callback to be triggered.

filterPreventDefault
filterPreventDefault: boolean // default: true

This flag determines if the default browser behavior should be prevented if the given filter function returns false and therefore prevents the execution of the callback. true is the default value, so each time the filter blocks the callback execution, the browser won't proceed with its default behavior. Setting this to false will allow the browser to proceed with the default behavior.

A good example for this behavior is the override of command+s, which normally triggers a save page dialog inside the browser.

useHotkeys('command+s', someCallback, {
// This will allow the browser to show the save page dialog.
// Setting it to true will prevent that.
filterPreventDefault: false,
filter: () => false
});
enabledOnTags
enabledOnTags: string[] // default: undefined

Normally we do not want a hotkey being triggered while a user types something into an input field. In some cases however this might desirable. We can enable the callback trigger for an input tag using the following values:

INPUT, TEXTAREA, SELECT

enabled
enabled: boolean // default: true

Setting this to false prevents the hook from doing anything.

splitKey
splitKey: string // default: "+"

Specifies the key that is used to combine multiple hotkeys into keystrokes. The default value is +, so shift+a triggers when the user presses the "shift" key and the "a" key.

Using "+" as a target key

Setting the splitKey to - to listen to a keystroke like + or shift-+ doesn't work. There is a bug in the implementation of the underlying hotkeys package that React Hotkeys Hook leverages. See this issue and this issue. This problem will be addressed in the upcoming Version 4.

keyup
keyup: boolean // default: false

Set this to true if we want the hook to trigger our callback on the browsers keyUp event.

keydown
keydown: boolean // default: true

Set this to true if we want the hook to trigger our callback on the browsers keyDown event. This is the default behavior.

Setting keydown and keyup

If we set keyup to true and don't set the keydown prop (leaving the default), React Hotkeys Hook will assume that we want to only listen to the browsers keyUp event.

If we in fact want the callback to get triggered by both events, we have to explicitly set both properties like so:

useHotkeys('a', () => someCallback, {
keydown: true,
keyup: true
})

deps

deps: any[] // default: []

The dependency array lets we use the hook just like Reacts internal useCallback or useMemo hook. This where our dependencies of the callback live. If for example our callback actions depend on a referentially unstable value or a value that will change over time, we should add this value to our deps array. Check out the documentation part for examples.

Return value

React.MutableRef<T | null>

The useHotkeys hook returns a React ref. This ref by default holds the value of null. We can use this ref to only trigger the hotkeys if a specific element has been focused by the user.

Live Editor
Result
Loading...
Not every html tag is able to receive focus by default

Elements that don't provide any native interactivity like <div>, <span>, <p>, etc. cannot receive a focus by default. If we want to use <div> tags instead of the <button> tags in the example above we have to provide a tabIndex prop to the tag. This way the focusing will work with all tags.

Live Editor
Result
Loading...

Function signature overloads

There is a common case where we want to pass dependencies to the hook but no options object. Normally we would need to write that out like this:

useHotkeys('a', () => someDependency, undefined, [someDependency]);

To streamline this use case the hook accepts function overloads. With this we can pass a dependency array as the third argument instead of the fourth.

function useHotkeys<T extends Element>(
keys: string,
callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
deps: any[] = []
): React.MutableRef<T | null>

So we are able to use the hook like this:

useHotkeys('a', () => someDependency, [someDependency]);