Skip to content
GitHubDiscord

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
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
.logConsole.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
('Bold shortcut pressed')
return true }, 'Shift-Enter': (statestate: EditorState, dispatchdispatch: ((tr: Transaction) => void) | undefined) => { consolevar console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
.logConsole.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
('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 {
  Priorityenum Priority
ProseKit extension priority.
@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>>, Priorityenum Priority
ProseKit extension priority.
@public
.highfunction (enum member) Priority.high = 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:

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-white dark:bg-gray-950 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.