Skip to main content
Version: 3.x

Disable/Enable Hotkeys

We are now able to register our hotkeys, scope them to the component (or part of a component) and prevent stale state inside our callback. But our hotkeys are always active. If the component has received focus, it will always trigger. However, sometimes we want to dynamically decide if the hotkey should even be enabled.

There are multiple ways to disable a hotkey. Let's go through all of them.

Dynamically disable or enable

Toggle hotkeys with the enabled flag

Using the enabled flag in the options object, we can dynamically tell useHotkeys if the hotkey should be active or not.

function ExampleComponent() {
const [enabled, setEnabled] = useState(false)
const [count, setCount] = useState(0)
useHotkeys('b', () => setCount(prevCount => prevCount + 1), {
enabled,
})

return (
<div>
<button onClick={() => setEnabled(prevValue => !prevValue)}>Toggle Hotkey</button>
<p>Hotkey is {!enabled && 'not'} enabled.</p>
<p>Pressed the 'b' key {count} times.</p>
</div>
)
}

This is useful to programmatically decide (depending on some internal state of our app or user input) if the hotkey should be active or not.

Tip

If we use the returned ref of the callback to scope our hotkey to a specific sub-tree of our component, note that this will automatically activate or deactivate our hotkey depending on which part of the page has active focus.


Prevent default browser behavior

Some hotkeys like ctrl + s are already assigned to a browser function, in this case saving a website to the users local disk. Sometimes we want to override these hotkeys to implement our own functionality. Maybe we want to build a text editor or some other user based creation tool that needs a save shortcut to save the current progress. It would be silly to introduce a new shortcut for saving the users progress, since every user knows that ctrl + s is the universal shortcut for saving.

Our callback receives the browsers native event object, so we can just prevent the default behavior there and set up our own custom handler:

function ExampleComponent() {
useHotkeys('cmd+s, ctrl+s', (e) => {
e.preventDefault()

alert('We saved your progress!')
// ... set up our own saving dialog.
})

return (
<div>
<p>Press cmd+s (or ctrl+s for non mac) to open custom save dialog.</p>
</div>
)
}
Warning

Please be aware that there are some hotkeys that we cannot override, because they would interfere with a safe browsing experience for the user. These depend on the browser. For example in Chrome, most notably those are cmd + w which closes a tab, cmd + n for opening a new window and cmd + t to open a new tab. Additionally cmd + shift + w (closing all tabs of the current window), cmd + shift + n (opening incognito window) and cmd + shift + t (reopen the last closed tab) cannot be overridden. cmd + up + 1..9 on the other hand focuses the corresponding tab of the active window and also cannot be overridden.


Disable callback execution

In addition to completely unbinding the hotkey, we can also disable the execution of the callback with setting the filter option. The filter key takes a function that is expected to return a boolean value to indicate if the callback should get executed or not. In this scenario the hotkey is still active, but the callback won't be called if the filter function returns false.

function ExampleComponent() {
useHotkeys('option+b', () => alert('Callback got executed'), {
filter: e => {
return true;
}
})

return (
<div>
<p>Pressing 'b' executes the callback, while 'alt+b' won't.</p>
</div>
)
}

With the filter option in mind we can slightly change our save routine override from above:

function ExampleComponent() {
useHotkeys('cmd+s, ctrl+s', () => alert('We saved your progress!'), {
filter: e => {
e.preventDefault()

return true
}
})

return (
<div>
<p>Press cmd+s (or ctrl+s for non mac) to open custom save dialog.</p>
</div>
)
}

This might be preferred if we want to build a custom hook on top of useHotkeys that always prevents the default behavior.


Disable default behavior when callback execution is blocked

The above example is sufficient for simple logic to determine whether the callback should be executed. However, we might come across a scenario where the filter function returns false so our callback does not get triggered. Maybe the logic which determines if the callback should be executed is pretty complex, and we don't want to determine inside the filter whether the browser should prevent its default behavior. To simplify this we can use the filterPreventDefault option:

function ExampleComponent() {
useHotkeys('cmd+s, ctrl+s', () => alert('We saved your progress!'), {
filter: e => {
// .... some logic to deduce if the callback should get triggered or not

// If no logic applies, don't trigger the callback
return false
},
filterPreventDefault: true,
})

return (
<div>
<p>Press cmd+s (or ctrl+s for non mac) to open custom save dialog.</p>
</div>
)
}

This will cause the browser to prevent opening its page saving dialog even though our callback has not been triggered.

Changes for version 4

Currently the return values of filter and filterPreventDefault are counterintuitive. This will change in version 4.


Enable hotkeys on form fields

By default, hotkeys won't trigger when the user is focusing a form field. This is the expected case most of the time. For example, if we'd listen for the hotkey 'p' on a review page for apps and a user would try to type This app is simply the best into the review input field, the resulting text would be This a is simly the best. Same with keystrokes that start with shift + ... since this normally capitalizes a letter. So it is good, that useHotkeys is smart enough to back off during focused form fields. However, sometimes you still want hotkeys to be enabled even when the user is focusing on an input field. There might be again the need so save the users progress. Our previous example would not trigger if the user is focusing a form field. Instead, we have to enable the hotkey on certain form fields. We can use the enableOnTags option for that which takes an array of form tags:

function ExampleComponent() {
useHotkeys('cmd+s, ctrl+s', (e) => {
e.preventDefault()

alert('We saved your progress!')
// ... set up our own saving dialog.
}, {
enableOnTags: ['INPUT', 'SELECT', 'TEXTAREA']
})

return (
<div>
<p>Press cmd+s (or ctrl+s for non mac) to open custom save dialog.</p>
</div>
)
}

We tell useHotkeys that the given keystrokes should be listened to and the callback executed even when the user is currently focusing an input, select or textarea field.

case-sensitive

Note that the values in the array are case-sensitive. input won't work while INPUT will. This likey to change in version 4.


Enable hotkeys on tags leveraging contentEditable

The contentEditable attribute allows users to edit text even though it is not part of an input field. For example a <div> tag could have the contentEditable attribute. Sometimes we want hotkeys to still trigger, even though a user is focusing an editing a tags content. A good example for this might be the editor on medium.com. useHotkeys provides an option for that:

function ExampleComponent() {
useHotkeys('cmd+b', () => alert('Hotkeys are still working'), {
enableOnContentEditable: true,
})

return (
<div contentEditable={true}>
You can edit this text and still use all the active shortcuts.
</div>
)
}
info

Before version 3.3.0, using a tag with contentEditable set to true would not prevent any hotkeys from triggering. Be sure to update to at least 3.4.0 to leverage this feature.