Frappe UIFrappe UI

KeyboardShortcutsModal & useShortcut

A composable + modal pair for registering, managing, and displaying keyboard shortcuts across an application.

useShortcut

Register one or more keyboard shortcuts inside a component. Shortcuts are automatically removed when the component unmounts (or is deactivated in a <KeepAlive> tree).

Basic usage

ts
import { useShortcut } from 'frappe-ui'

useShortcut({
  key: 's',
  ctrl: true,
  description: 'Save document',
  group: 'General',
  handler: () => save(),
})

Multiple shortcuts at once

ts
useShortcut([
  {
    key: 'z',
    ctrl: true,
    description: 'Undo',
    group: 'Edit',
    handler: () => undo(),
  },
  {
    key: 'z',
    ctrl: true,
    shift: true,
    description: 'Redo',
    group: 'Edit',
    handler: () => redo(),
  },
])

Conditional shortcut

ts
useShortcut({
  key: 'Delete',
  description: 'Delete selected block',
  group: 'Canvas',
  condition: () => !!selectedBlock.value,
  handler: () => deleteBlock(selectedBlock.value),
})

Hold-to-activate mode

ts
useShortcut({
  key: ' ',
  description: 'Hold for move mode',
  group: 'Tools',
  triggeredOn: 'hold',
  onHold: () => (mode.value = 'move'),
  onRelease: () => (mode.value = 'select'),
})

ShortcutConfig options

OptionTypeDefaultDescription
keystringKey to listen for (e.g. "s", "Escape", "ArrowUp")
ctrlbooleanfalseRequire Ctrl (or ⌘ on Mac)
shiftbooleanfalseRequire Shift
descriptionstringHuman-readable label shown in the shortcuts modal
groupstring"General"Group name used to categorise shortcuts
triggeredOn"press" | "hold""press"When to fire the handler
handler(e) => voidCalled on keydown when the shortcut matches
onHold(e) => voidCalled on first keydown while held (triggeredOn: "hold")
onRelease(e) => voidCalled on keyup when the held combo is released
preventDefaultbooleantruePrevent the browser's default action
allowInInputbooleanfalseAllow the shortcut to fire inside inputs / textareas
allowInDialogbooleanfalseAllow the shortcut to fire when focus is inside a [role="dialog"] element. Set to true for shortcuts that are intentionally scoped to a dialog (e.g. an Escape handler or ? help shortcut)
condition() => booleanShortcut only fires when this returns true

KeyboardShortcutsModal

A dialog that lists all currently active shortcuts, grouped and searchable. Reads its data from the global useShortcut registry automatically.

Mount once in your app root

vue
<template>
  <KeyboardShortcutsModal v-model:open="shortcutsModalOpen" />
</template>

<script setup lang="ts">
import { KeyboardShortcutsModal } from 'frappe-ui'
import { ref } from 'vue'

const shortcutsModalOpen = ref(false)

function openShortcuts() {
  shortcutsModalOpen.value = true
}
</script>

Open via keyboard shortcut

ts
import { useShortcut, KeyboardShortcutsModal } from 'frappe-ui'
import { ref } from 'vue'

const shortcutsModalOpen = ref(false)

useShortcut({
  key: '?',
  description: 'Show keyboard shortcuts',
  group: 'General',
  allowInDialog: true,
  handler: () => {
    shortcutsModalOpen.value = true
  },
})

Props

PropTypeDefaultDescription
openbooleanfalseControls dialog visibility — use with v-model:open
titlestring"Keyboard Shortcuts"Dialog title
paddingTopstring"5vh"Top padding when dialog position is top
searchThresholdnumber20Show the search input when this many shortcuts are registered

getActiveShortcuts

Lower-level helper that returns a computed list of all currently registered shortcuts whose conditions are met. Duplicate registrations with the same group + description + modifiers are merged into a single entry with multiple keys. This is what KeyboardShortcutsModal consumes internally.

ts
import { getActiveShortcuts } from 'frappe-ui'

const shortcuts = getActiveShortcuts()
// shortcuts.value → ActiveShortcut[]

formatShortcutLabel

Returns a short human-readable string for a shortcut (e.g. "⌘ S" on Mac or "Ctrl + S" on Windows).

ts
import { formatShortcutLabel } from 'frappe-ui'

const label = formatShortcutLabel({ key: 's', ctrl: true })
// → "⌘ S" (macOS) or "Ctrl + S" (Windows/Linux)