Learn to Implement Custom Hotkeys with the Tanstack Hotkey Library
Integrating custom keyboard shortcuts, or hotkeys, can significantly enhance user experience on your website. However, building this functionality from scratch can be complex and time-consuming. The Tanstack hotkey library simplifies this process, making it incredibly easy to add powerful hotkey features to your web applications. This guide will walk you through various examples, from basic setups to advanced features like recording and saving user-defined hotkeys.
What You’ll Learn
This tutorial will cover the following:
- How to use the
useHotkeyhook for basic and advanced hotkey configurations. - Understanding modifier keys (
mod) for cross-platform compatibility (Windows/Mac). - Leveraging TypeScript for type safety and autocompletion with hotkeys.
- Configuring hotkey options such as
target,enabled,conflictBehavior, and more. - Using
useHeldKeysanduseKeyHoldhooks to track currently pressed keys. - Implementing sequential hotkeys using arrays of key combinations.
- Building a user-configurable hotkey system with the
useHotkeyRecorderhook.
Prerequisites
- Basic understanding of React and JavaScript.
- Familiarity with TypeScript (helpful but not strictly required for basic usage).
- A modern web development environment set up (e.g., Create React App, Vite).
Step 1: Setting Up the Tanstack Hotkey Library
First, you need to install the library. Open your terminal in your project directory and run:
npm install @tanstack/react-hotkeys
# or
yarn add @tanstack/react-hotkeysStep 2: Implementing Basic Hotkeys with useHotkey
The core of the library is the useHotkey hook. It takes a hotkey combination and a callback function as arguments.
Understanding Hotkey Syntax
You can define hotkeys using string combinations. For example, 'control+s' represents pressing the Control key and the ‘S’ key simultaneously.
Using the mod Modifier
To ensure your hotkeys work correctly across different operating systems, use the mod keyword. On Windows and Linux, mod translates to the Ctrl key. On macOS, it translates to the Command key.
Example:
import { useHotkey } from '@tanstack/react-hotkeys';
import React, { useState } from 'react';
function App() {
const [log, setLog] = useState('');
useHotkey('mod+s', () => {
const timestamp = new Date().toLocaleTimeString();
setLog(`Saved at ${timestamp}`);
});
useHotkey('mod+z', () => {
const timestamp = new Date().toLocaleTimeString();
setLog(`Undo at ${timestamp}`);
});
useHotkey('mod+shift+z', () => {
const timestamp = new Date().toLocaleTimeString();
setLog(`Redo at ${timestamp}`);
});
return (
Hotkey Example
Press Ctrl+S (or Cmd+S on Mac) to save.
Press Ctrl+Z (or Cmd+Z on Mac) to undo.
Press Ctrl+Shift+Z (or Cmd+Shift+Z on Mac) to redo.
Log: {log}
);
}
export default App;
TypeScript Safety and Autocompletion
The library provides excellent TypeScript support. You get autocompletion for valid hotkey combinations and type safety, preventing typos and ensuring correct syntax.
Formatting Hotkeys for Display
The library can format hotkeys to match the conventions of the user’s operating system using the formatHotkey function (though this is often handled implicitly by the library in UI rendering). This means mod+s will display as Ctrl + S on Windows and Command + S on Mac.
Expert Note: While the library handles platform-specific display, you can manually implement custom type safety using TypeScript’s utility types. This involves defining types for keys, modifiers, and their combinations using string literal types and template literals. This is an advanced technique for ensuring maximum type safety in your hotkey definitions.
Step 3: Advanced Hotkey Options
The useHotkey hook accepts an optional third argument: an options object. This allows for more granular control over how and when hotkeys are triggered.
target: Scoping Hotkeys
You can specify a DOM element as the target for a hotkey. The hotkey will only be active when the target element (or its descendants) has focus.
// Hotkey only works when the element with id 'my-input' is focused
useHotkey('enter', () => console.log('Enter pressed'), { target: '#my-input' });
// Global hotkey (no target specified or target is document)
useHotkey('mod+s', () => console.log('Global save'), { target: document });
enabled: Toggling Hotkeys
Use the enabled option to easily enable or disable a hotkey dynamically.
const [isEscapeEnabled, setIsEscapeEnabled] = useState(true);
useHotkey('escape', () => console.log('Escape pressed'), {
enabled: isEscapeEnabled,
});
// Later, to disable:
// setIsEscapeEnabled(false);
conflictBehavior: Handling Duplicate Hotkeys
This option determines how the library reacts if you define multiple hotkeys for the same key combination. Options include 'allow', 'warn', 'error', and 'replace'.
eventType: Key Down vs. Key Up
By default, hotkeys trigger on 'keydown'. You can change this to 'keyup' if needed.
useHotkey('a', () => console.log('Key A released'), { eventType: 'keyup' });
ignoreInputs: Preventing Interference with Form Elements
When ignoreInputs is enabled (defaults to a smart behavior), hotkeys might be ignored when focus is within input elements (like , ) to avoid conflicts with typing. Set to true to always prioritize hotkeys, or false to always respect input behavior.
preventDefault and stopPropagation
By default, preventDefault is true, stopping the browser’s default action for the key combination (e.g., Ctrl+S usually opens a save dialog). stopPropagation prevents the event from bubbling up the DOM tree.
requireReset: Preventing Repeated Fires
If set to true, a hotkey will only trigger once per key press. The user must release and re-press the key for it to fire again. This is highly recommended for most hotkeys to avoid unintended multiple executions.
Step 4: Tracking Held Keys with useHeldKeys and useKeyHold
These hooks are useful for scenarios where you need to know which keys are currently being held down.
useHeldKeys
Returns an array of strings representing all currently held keys.
useKeyHold
Returns a boolean indicating whether a specific key is currently held down.
Step 5: Implementing Sequential Hotkeys
For hotkeys that require pressing multiple keys in sequence (like in Vim or VS Code), you can pass an array of hotkey combinations to useHotkey.
timeout Option
You can configure the time window for sequence completion using the timeout option.
Step 6: Creating User-Configurable Hotkeys with useHotkeyRecorder
This advanced hook allows users to define and change hotkeys within your application.
How it Works
The useHotkeyRecorder hook provides functions to start, stop, and cancel the recording of a hotkey. It also provides callbacks for when a hotkey is recorded and when recording is cancelled.
The process generally involves:
- Rendering UI elements (e.g., buttons) to initiate recording for specific actions.
- When a record button is clicked, call
recorder.startRecording(). - The library listens for key presses. Once a combination is detected, it’s passed to your
onRecordhandler. - Your
onRecordhandler saves the new hotkey combination (e.g., to state or local storage). - This new hotkey is then used to configure a
useHotkeyinstance for that action. - You can also provide an
onCancelcallback for when the user aborts the recording.
This enables a powerful feature where users can customize their shortcuts, making your application more accessible and user-friendly. The actual implementation involves managing state for the recorded hotkeys and dynamically applying them with useHotkey.
Example Snippet (Conceptual)
The Tanstack hotkey library offers a robust and flexible solution for implementing keyboard shortcuts in your React applications. From simple key triggers to complex, user-defined configurations, it provides the tools to significantly improve your application’s usability.
Source: NEW Tanstack Hotkeys Library is Amazing (YouTube)