@svelte-put/shortcut

GitHub Github

add keyboard shortcuts to node

@svelte-put/shortcut @svelte-put/shortcut @svelte-put/shortcut @svelte-put/shortcut

Still on Svelte 4? See the old docs site here.

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

Available since v4.1.0.

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',
  }}
/>

Original KeyboardEvent

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! 👨‍💻

Edit this page on Github