@svelte-put/shortcut
GithubCompatible with or powered directly by Svelte runes.
Installation
npm install --save-dev @svelte-put/shortcut
pnpm add -D @svelte-put/shortcut
yarn add -D @svelte-put/shortcut
New to Svelte 5? See Migration Guides.
Quick Start
The minimal example below shows how to register a global shortcut for Ctrl + K
(Cmd + K
on macOS).
<script lang="ts">
import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';
function handleK(detail: ShortcutEventDetail) {
console.log('attached node:', detail.node);
console.log('original trigger config:', detail.trigger);
}
</script>
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
callback: handleK,
},
}}
/>
Trigger
The trigger
option take either one single trigger definition...
<svelte:window
use:shortcut={{ trigger: { key: 'k', modifier: 'ctrl' } }}
/>
...or multiple ones in an array...
<svelte:window
use:shortcut={{
trigger: [
{ key: 'c', modifier: 'ctrl' },
{ key: 'v', modifier: 'ctrl' },
],
}}
/>
You can use multiple use:shortcut
with one trigger definition in each.
<svelte:window
use:shortcut={{ trigger: { key: 'c', modifier: 'ctrl' } }}
use:shortcut={{ trigger: { key: 'v', modifier: 'ctrl' } }}
/>
This approach does take up some additional memory. It should be negligible in most cases but if your application is performance-critical, it is recommend to provide all triggers in one use:shortcut
as shown in the multiple-triggers.svelte
example.
Modifier
Each ShortcutTrigger
can specify either one or multiple modifiers (ctrl
, meta
, alt
, shift
) via trigger.modifier
in both AND
& OR
fashions.
Catch all
When left as undefined
, trigger.modifier
means "don't check for modifiers".
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
},
}}
/>
No modifier
Set trigger.modifier
to false
or null
for keys that expect no modifier.
<svelte:window
use:shortcut={{
trigger: {
key: 'Escape',
modifier: false,
},
}}
/>
Single Modifier
Set trigger.modifier
to one of the modifiers (ctrl
, meta
, alt
, shift
) to listen for that modifier.
<svelte:window
use:shortcut={{
// ctrl+k
trigger: {
key: 'k',
modifier: 'ctrl',
},
}}
/>
One of Many Modifiers (OR)
Use a flat array of modifiers to trigger when one of them is pressed.
<svelte:window
use:shortcut={{
// ctrl+k or meta+k
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
},
}}
/>
All of Modifiers (AND)
Wrap multiple modifiers in a subarray to trigger only when all modifiers are pressed at the same time.
<svelte:window
use:shortcut={{
// ctrl+shift+K
trigger: {
key: 'K',
modifier: [['ctrl', 'shift']],
},
}}
/>
Notice that the key
option in the above example is set to a capital K. This is because shift
is specified as a modifier; when shift
and k
are pressed down at the same time, KeyboardEvent.key value will be K
. You can use this site to test your key combinations.
Mix & Match
Use a combination of OR
and AND
to create complex modifier combinations.
<svelte:window
use:shortcut={{
// ctrl+alt+Delete or meta+Delete
trigger: {
key: 'Delete',
modifier: [['ctrl', 'alt'], 'meta'],
},
}}
/>
Handler
Handlers can be provided via either trigger.callback
...
<script lang="ts">
import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';
function toggleCommandPalette(detail: ShortcutEventDetail) {
console.log('Action was placed on:', details.node);
console.log('Trigger:', details.trigger);
// ...
}
</script>
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
callback: toggleCommandPalette,
},
}}
/>
...or onshortcut
CustomEvent:
<script lang="ts">
import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';
function handleShortcuts(event: CustomEvent<ShortcutEventDetail>) {
if (event.detail.trigger.id === 'toggle-command-palette') {
// ...
}
}
</script>
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
id: 'toggle-command-palette',
},
}}
onshortcut={handleShortcuts}
/>
trigger.id
is specified in the handler-custom-event.svelte
example to conveniently help identify the trigger in the onshortcut
event listener.
The two approaches are equivalent and depend on your aesthetic preference when multiple shortcuts are defined. trigger.callback
is bound directly to its trigger definition, whereas onshortcut
is a centralized event listener for all shortcuts.
You should use only one of the two presented approaches and NOT both to avoid duplicate event handling.
Event Type
@svelte-put/shortcut
support keydown
(default) or keyup
event type. You may change this via
the type
option.
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
},
type: 'keyup',
}}
/>
KeyboardEvent
Original You can access the original KeyboardEvent
via detail.originalEvent
. This is helpful for checking target
.
<script lang="ts">
import type { ShortcutEventDetail } from '@svelte-put/shortcut';
import { shortcut } from '@svelte-put/shortcut';
function onShortcut(event: CustomEvent<ShortcutEventDetail>) {
const keyboardEvent = event.detail.originalEvent;
// be cautious: `keyboardEvent` has already reached window here
keyboardEvent.preventDefault(); // prevent browser default
if ((keyboardEvent.target as HTMLElement)?.tagName === 'INPUT') {
console.log('some input is focused, should skip');
return;
}
// do things
}
</script>
<svelte:window
use:shortcut={{
trigger: {
key: 'k',
modifier: ['ctrl', 'meta'],
},
}}
onshortcut={onShortcut}
/>
Be aware that the event listener is placed on the same node the shortcut
action is attached to. For example, if you use the action on svelte:window
, when a shortcut is triggered from an <input>
element, calling stopPropagation
or preventDefault
on originalEvent
might not result in the behavior you would expected. By the time trigger.callback
or onshortcut
event handler runs, the event has already bubbled up to window
.
API References
It is recommended to utilize your language server and intellisense for API exploration. In advanced use cases, however, you can refer to shortcut's type definitions.
Migration Guides
V3 -> V4 (Svelte 5 in Runes mode)
When migrating to V4, you will need to change event directive on:shortcut
to standard attribute onshortcut
(remove :
).
<div use:shortcut on:shortcut></div>
<div use:shortcut onshortcut></div>
Happy making shortcuts! 👨💻