Skip to content

Keyboard Shortcuts

Add keyboard shortcuts to your editor using the defineKeymap function or the useKeymap hook for framework-specific implementations.

Define custom keyboard shortcuts using the defineKeymap function:

import { defineKeymapfunction defineKeymap(keymap: Keymap): PlainExtension
Adds a set of keybindings to the editor. Please read the [documentation](https://prosemirror.net/docs/ref/#keymap) for more details.
@public
} from 'prosekit/core'
const extensionconst extension: PlainExtension = defineKeymapfunction defineKeymap(keymap: Keymap): PlainExtension
Adds a set of keybindings to the editor. Please read the [documentation](https://prosemirror.net/docs/ref/#keymap) for more details.
@public
({
'Mod-b': (statestate: EditorState, dispatchdispatch: ((tr: Transaction) => void) | undefined) => { consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
('Bold shortcut pressed')
return true }, 'Shift-Enter': (statestate: EditorState, dispatchdispatch: ((tr: Transaction) => void) | undefined) => { consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
('Shift-Enter pressed')
return true }, })

defineKeymap accepts an object where keys are keyboard shortcuts and values are Command functions to execute.

Key names are strings like Shift-Ctrl-Enter, consisting of zero or more modifiers followed by a KeyboardEvent.key key name.

Supported modifiers:

  • "Ctrl": Ctrl key
  • "Alt": Alt key (equivalent to Option on macOS)
  • "Shift": Shift key
  • "Mod": Command on macOS, Ctrl on other platforms
  • "Meta": Command on macOS, Win on Windows

A KeyboardEvent.key string. Use lowercase letters for letter keys, or uppercase letters when shift should be held. When using uppercase letters, the "Shift" modifier is implicit and must not be included separately. "Space" is an alias for " ".

  • "Enter": ✅ Triggers when Enter is pressed
  • "Shift-Enter": ✅ Triggers when ShiftEnter is pressed
  • "Shift-enter": ❌ Invalid — "enter" is not a valid KeyboardEvent.key value
  • "Ctrl-a": ✅ Triggers when CtrlA is pressed
  • "Ctrl-A": ✅ Triggers when CtrlShiftA is pressed
  • "Ctrl-Shift-A": ❌ Invalid — "Shift" modifier is implicit with uppercase letters
  • "Ctrl-Shift-a": ❌ Invalid — when Shift is held, KeyboardEvent.key is "A", not "a"
  • "Shift- ": ✅ Triggers when ShiftSpace is pressed
  • "Shift-Space": ✅ Valid (equivalent to the above)

Call defineKeymap multiple times to define additional shortcuts. Later definitions override earlier ones.

Use withPriority to set extension priority:

import { Priority
type Priority = 0 | 1 | 2 | 3 | 4
const Priority: {
    readonly lowest: 0;
    readonly low: 1;
    readonly default: 2;
    readonly high: 3;
    readonly highest: 4;
}
ProseKit extension priority. There are five priority levels available: - `Priority.lowest` - `Priority.low` - `Priority.default` - `Priority.high` - `Priority.highest`
@example```ts import { withPriority, Priority } from 'prosekit/core' import { myExtension } from './my-extension.js' const myExtensionWithHighPriority = withPriority(myExtension, Priority.high) ```@public@example```ts import { withPriority, Priority } from 'prosekit/core' import { myExtension } from './my-extension.js' const myExtensionWithHighPriority = withPriority(myExtension, Priority.high) ```@public
, withPriorityfunction withPriority<T extends Extension>(extension: T, priority: Priority): T
Return an new extension with the given priority.
@example```ts import { Priority, withPriority } from 'prosekit/core' const extension = withPriority(defineMyExtension(), Priority.high) ```@public
} from 'prosekit/core'
const extensionAPrioritizedconst extensionAPrioritized: Extension<ExtensionTyping<any, any, any>> = withPrioritywithPriority<Extension<ExtensionTyping<any, any, any>>>(extension: Extension<ExtensionTyping<any, any, any>>, priority: Priority): Extension<ExtensionTyping<any, any, any>>
Return an new extension with the given priority.
@example```ts import { Priority, withPriority } from 'prosekit/core' const extension = withPriority(defineMyExtension(), Priority.high) ```@public
(extensionAconst extensionA: Extension<ExtensionTyping<any, any, any>>, Priority
const Priority: {
    readonly lowest: 0;
    readonly low: 1;
    readonly default: 2;
    readonly high: 3;
    readonly highest: 4;
}
ProseKit extension priority. There are five priority levels available: - `Priority.lowest` - `Priority.low` - `Priority.default` - `Priority.high` - `Priority.highest`
@example```ts import { withPriority, Priority } from 'prosekit/core' import { myExtension } from './my-extension.js' const myExtensionWithHighPriority = withPriority(myExtension, Priority.high) ```@public@example```ts import { withPriority, Priority } from 'prosekit/core' import { myExtension } from './my-extension.js' const myExtensionWithHighPriority = withPriority(myExtension, Priority.high) ```@public
.highhigh: 3)

useKeymap is available for UI framework integrations. This is useful when you want to define shortcuts dynamically.

An example of how to use useKeymap to define shortcuts:

'use client'

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'

import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/react'
import { useCallback, useMemo, useState } from 'react'

import { defineExtension } from './extension'
import Toolbar from './toolbar'

export default function Editor() {
  const editor = useMemo(() => {
    const extension = defineExtension()
    return createEditor({ extension })
  }, [])

  const [submissions, setSubmissions] = useState<string[]>([])

  const pushSubmission = useCallback(
    (hotkey: string) => {
      const docString = JSON.stringify(editor.getDocJSON())
      const submission = `${new Date().toISOString()}\t${hotkey}\n${docString}`
      setSubmissions((prev) => [...prev, submission])
    },
    [editor],
  )

  return (
    <ProseKit editor={editor}>
      <div className="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-[canvas] text-black dark:text-white">
        <Toolbar onSubmit={pushSubmission} />
        <div className="relative w-full flex-1 box-border overflow-y-auto">
          <div ref={editor.mount} className="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"></div>
        </div>
      </div>
      <fieldset className="mt-4 box-border flex max-w-full w-full overflow-x-auto border p-4 rounded-md shadow-sm min-w-0">
        <legend>Submit Records</legend>
        <ol>
          {submissions.map((submission, index) => (
            <li key={index}>
              <pre>{submission}</pre>
            </li>
          ))}
        </ol>
        {submissions.length === 0 && <div>No submissions yet</div>}
      </fieldset>
    </ProseKit>
  )
}

defineBaseKeymap includes the base keyboard shortcuts for the editor. You would likely want to include this in your extension. defineBaseKeymap is already included in the defineBasicExtension function.