Building hotkeys in React apps
Tags: react, typescript, javascript, ux
Published: 2022-11-08 13:08:01

## Introduction

Using hotkeys is a great way to let your web app users get more done faster.

It works even better for power users and complex internal applications where your users spend a lot of time, like systems for content editing, monitoring systems or support tools.

In this article, first, we'll look at some design aspects to keep in mind when implementing hotkeys in an app and ways to make hotkeys convenient for the end user. Then we will do basic hotkeys implementation and look at how to add hotkeys to React application.

## Hotkey Integration Preparations

Stick to three hotkey development guidelines:

- Discoverability: familiarise your users with hotkeys. Use onboarding, walkthroughs, labels or tooltips to help users learn about your hotkeys system
- Memorability: hotkeys and key combos need to be simple and memorable
- Compatibility: avoid situations where your hotkeys conflict with existing system-wide or browser shortcuts


## 1. Make a Plan 

Define a list of actions demanding hotkey implementation. It is helpful to pursue a structured approach, starting with mapping out the user stories in your application and defining app sections. Then split your user stories into actions ranked by importance and usage frequency. Leveraging user feedback and analytics can be very helpful here. All the actions could be divided in two big groups:

- Global actions include navigation between app parts and launching important functionality. Generally, it makes sense to implement it on the top level of the app (e.g. as a react wrapper component or context).

- Contextual actions, such as controlling modal windows or popups, should be implemented at the relevant component level (modal window, multi-step form, etc.).

## 2. Pick the Hotkeys

Think of the hotkeys and key combos. Remember that they should be memorable and non-conflicting with browser or OS hotkeys. 

- Keep context and users in mind. What OS they are using, other running apps, preferred browser hotkeys, such as native browser search for content-rich websites.

- Steer clear of the overloaded classic key combos
Ctrl + N, Ctrl + O, Ctrl + S, Ctrl + P, Ctrl + W & Ctrl + C, Ctrl + Z, Ctrl + F
Alt + F1, F2, F3…

- Decide if you want to use
  - Single hotkeys
  - Combinations or
  - Sequences - keep in mind that they should not conflict with single hotkeys

Settling on one approach will simplify hotkeys for the users. 

Single-key hotkeys are easier to use and remember, especially if they're assigned to actions corresponding with key letters. However, this allows for accidental keypresses and a smaller number of possible hotkeys. There are more options with combinations, but some of them will be already used by OS and browser hotkeys. 

The sequences approach works for assigning lots of actions, giving you the biggest freedom of options and number of available combos. For example, it’s used in Gmail. But if you pick this route, avoid using single hotkeys since that will lead to conflicts and a poor UX. You can override browser hotkeys by calling `event.preventDefault()` but think twice before you do it as it can be super annoying to your users sometimes.

After deciding what hotkeys you will assign to your actions, go to your app and try to use them as if they are already there. This will put you in users' shoes and let you identify what works well and what could be improved.

## 3. Think About Informing the User 

Find a way to implement hotkey information in your design. Some of the frequently used tools include:

**Labels**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl76bt71p3ocp22flcnq.png)

**Tooltips**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ok07f1hdj43fjd1fuee2.png)

**Underlined letters in buttons corresponding to hotkeys**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rei8u8slmryh9qkvsvu6.png)

**Hotkey maps** 
For the best discoverability and memorability, it’s great to add a hotkey map, i.e. a page or a  modal window with the list of all available actions in the app.

If there are many hotkeys, a search with a filter may be added on top of the map. Here are some examples of hotkey maps:
1. [Pivotal tracker ](https://www.pivotaltracker.com/help/articles/keyboard_shortcuts/)
2. [Trello](https://trello.com/shortcuts)
3. [Codesandbox](https://codesandbox.io/docs/learn/repositories/shortcuts)

By the way, in all the examples above, users can see those hotkey maps by hitting a '?' key. You can consider following this best practice as well. 

As a result, you have a list of hotkeys and informational design elements to get users up to speed. Now we can move on to implementation.

For internal app interfaces, which users think of as their main work tools, consider making hotkeys customisable so that users can reassign the keys. This could be useful for the most advanced users.

## Implementing Hotkeys in React

Let’s dive into how hotkeys can be implemented in a React app, look into possible improvements and go over some popular hotkeys libraries that could be helpful to build hotkeys system for your app.

### Adding Hotkeys

This is what the most basic TypeScript hotkey implementation looks like. All hotkeys implementations are based on event listener, attached either to the document object or a specific DOM node. 

We can use keydownevent to track keyboard presses by the user. Note that we’ve added a check for the `event.repeat` property to neutralise double clicks.

You can always check out all keyboardevents properties on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#properties). 

```typescript
export const setupHotkeys = () => {
 document.addEventListener("keydown", (event: KeyboardEvent) => {
   if (event.repeat) {
     return;
   }
   if (event.ctrlKey && event.key === "l") {
     // do what you want to do when CTRL + L is pressed 
   }
 });
};
```

### Hotkeys in a React App
Now let’s implement basic hotkeys as a hook for a React app. 

We are using the `useLayoutEffect` hook and useRef hooks to recall the most up to date callback to avoid bugs. Here is a very nice [article](https://kentcdodds.com/blog/useeffect-vs-uselayouteffect) explaining how `useLayoutEffect` hook works just in case.

We’re also using the `useEffect` callback to attach the listener. Please note that we are removing the event listener in a function that gets returned by the `useEffect` hook. (Here is the [documentation ](https://reactjs.org/docs/hooks-effect.html) for `useEffect` hook). We do this to ensure we don’t leave the listener attached when the component is removed from dom - this is needed to avoid memory leaks.

```typescript
export const useHotkeys = (
 key: string,
 callback: (event: KeyboardEvent) => void
) => {
 const callbackRef = useRef(callback);
 
 useLayoutEffect(() => {
   callbackRef.current = callback;
 });
 
 useEffect(() => {
   const handler = (event: KeyboardEvent) => {
     if (event.key === key) {
       callbackRef.current(event);
     }
   };
 
   document.addEventListener("keydown", handler);
 
   return () => document.removeEventListener("keydown", handler);
 }, [key]);
};

```

Let’s now implement support for combos.

We start by adding an additional parameter, like a combination key or a modifier - Alt, Ctrl, Shift, and the meta key (Windows logo key or the Mac command key). To support the combination, we’ll add an additional check in the handler so that the app reacts only when the key is pressed in a combination with a modifier key.

```typescript
type CombinationKey = "alt" | "ctrl" | "meta" | "shift";
 
export const useHotkeys = (
 key: string,
 combinationKey: null | CombinationKey,
 callback: (event: KeyboardEvent) => void
) => {
 const callbackRef = useRef(callback);
 
 useLayoutEffect(() => {
   callbackRef.current = callback;
 });
 
 useEffect(() => {
   const handler = (event: KeyboardEvent) => {
     if (
       (!combinationKey || event[`${combinationKey}Key`]) &&
       event.key === key
     ) {
       callbackRef.current(event);
     }
   };
 
   document.addEventListener("keydown", handler);
   return () => document.removeEventListener("keydown", handler);
 }, [key, combinationKey]);
};
 
```

## React Libraries for Hotkeys

There are lots of hotkey development libraries for React apps, one of the most popular ones being `react-hotkeys-hook`.

An example of how hotkey implementation works in this library:

```typescript
import { useHotkeys } from 'react-hotkeys-hook'

export const Component = () => {
 useHotkeys('ctrl+j', () => {/* do something */});

 return (
   /* some jsx */
   )
}
```

Here’s some other hotkey libraries to check out:

- https://github.com/jaywcjlove/react-hotkeys
- https://gitlab.com/iamnautilusjungle/mousetrap-react
- Hotkey functionality can also be implemented using other libraries, such as Blueprint.js: https://blueprintjs.com/docs/#core/hooks/use-hotkeys

## Conclusion

We looked at some ways of building and implementing hotkey systems in React apps. When developing hotkeys, our priority should always be usability and comfort. Well-designed hotkeys can save tons of time for your users and make them happy so it's worth considering hotkeys in your app.

If you found this interesting and want to know more, here are some cool articles on this topic that I've used while researching:

- [Keyboard Shortcuts Creation](https://blog.prototypr.io/keyboard-shortcuts-creation-d5ae2663fdea)
- [J, K, or How to choose keyboard shortcuts for web applications](https://sashika.medium.com/j-k-or-how-to-choose-keyboard-shortcuts-for-web-applications-a7c3b7b408ee)
- [How to design great keyboard shortcuts](https://knock.app/blog/how-to-design-great-keyboard-shortcuts)