Register keyboard shortcuts and show the right keys for the user's platform.

useShortcut registers a shortcut while your component is mounted. For a component that already accepts shortcut, such as a dialog trigger, use that prop directly.

<script lang="ts">
  import { useShortcut, useShortcutHint } from "@ui-sdk/svelte/shortcuts"

  let status = $state("Press N or the keys to save.")
  const newNote = () => (status = "Note created")
  const save = () => (status = "Saved")

  useShortcut("n", newNote, { description: "New note" })
  useShortcut("mod+s", save, { description: "Save" })
  const saveHint = useShortcutHint("mod+s")
</script>

<div data-slot="stack">
  <div data-slot="row">
    <button type="button" aria-keyshortcuts="N" onclick={newNote}>
      New note
      <kbd aria-hidden="true" data-slot="kbd">N</kbd>
    </button>
    <button
      type="button"
      aria-keyshortcuts={saveHint.ariaKeyshortcuts}
      onclick={save}
    >
      Save
      <kbd aria-hidden="true" data-slot="kbd">{saveHint.text}</kbd>
    </button>
  </div>
  <output data-slot="output">{status}</output>
</div>
Press N to create a note. Press Command+S on Mac or Control+S elsewhere to save. The buttons run the same actions.

Register a Shortcut

Keys are joined with +. mod means Command on Apple platforms and Control elsewhere. This example also displays the keys using useShortcutHint:

<script lang="ts">
  import { useShortcut, useShortcutHint } from "@ui-sdk/svelte/shortcuts"

  let count = $state(0)
  const addNote = () => count++
  useShortcut("mod+enter", addNote, { description: "New note" })
  const hint = useShortcutHint("mod+enter")
</script>

<button onclick={addNote} aria-keyshortcuts={hint.ariaKeyshortcuts}>
  New note ({count}) <kbd aria-hidden="true">{hint.text}</kbd>
</button>

Call the helper in the component's script. It unregisters when the component goes away. Keys and options can be getters; return null for keys to leave the shortcut unregistered.

Show the Keys

useShortcutHint supplies display text and aria-keyshortcuts. It keeps the server and first client render consistent, then updates for the user's platform after mount.

Its fields are reactive getters. Read hint.text and hint.ariaKeyshortcuts in markup.

Outside rendering, use formatShortcut for display text or ariaKeyshortcuts for the ARIA value, from the same entry.

While Someone Is Typing

Shortcuts with Command, Control, or Option can run in text fields. Plain letters and other keys stay available for typing. Set allowInInput: true when a shortcut should work there too, such as Escape to clear a search.

Limit a Shortcut to an Area

Use a named scope for an editor or another area. Turn it on while that area has focus:

<script lang="ts">
  import { shortcuts, useShortcut } from "@ui-sdk/svelte/shortcuts"
  import { onDestroy } from "svelte"

  let text = $state("")
  useShortcut(
    "escape",
    () => {
      text = ""
    },
    { scope: "note", allowInInput: true }
  )
  onDestroy(() => shortcuts.setScope("note", false))
</script>

<textarea
  aria-label="Note"
  bind:value={text}
  onfocus={() => shortcuts.setScope("note", true)}
  onblur={() => shortcuts.setScope("note", false)}
/>

Scope names are shared, so choose one for each area you want to enable together. A when function can add another condition, checked each time the keys are pressed.

<script lang="ts">
  import { shortcuts, useShortcut } from "@ui-sdk/svelte/shortcuts"

  let text = $state("")
  let status = $state("Focus the note to turn on its shortcuts.")

  useShortcut(
    "mod+enter",
    () => (status = `Characters saved: ${text.trim().length}`),
    {
      scope: "note",
      when: () => text.trim() !== "",
      description: "Save the note",
    }
  )
  useShortcut(
    "escape",
    () => {
      text = ""
      status = "Cleared"
    },
    { scope: "note", allowInInput: true, description: "Clear the note" }
  )
</script>

<div data-slot="stack">
  <textarea
    bind:value={text}
    aria-label="Note"
    placeholder="Write a note…"
    onfocus={() => shortcuts.setScope("note", true)}
    onblur={() => shortcuts.setScope("note", false)}></textarea>
  <output data-slot="output">{status}</output>
</div>
Focus the note and type some text. Press Command+Enter on Mac or Control+Enter elsewhere to save, or Escape to clear it. Move focus outside the note to turn off its shortcuts.

Inside a Popup

Page shortcuts pause while a modal is open. Pass layer, an element inside the popup or a getter that returns one, to make a shortcut belong to that popup. It runs only while that popup is open.

When shortcuts share the same keys, the topmost open layer wins, then the most recently registered shortcut.

List Available Shortcuts

Call shortcuts.list() when a help screen opens. It returns the shortcuts that can run now, including their keys, formatted hint, and description. Add descriptions when registering shortcuts so the help screen can explain each action.