Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/prosekit/prosekit/llms.txt

Use this file to discover all available pages before exploring further.

Keyboard shortcuts make your editor more efficient and user-friendly. This guide shows you how to add keyboard shortcuts to your editor using the defineKeymap function or the useKeymap hook for framework-specific implementations.

Using defineKeymap

Define custom keyboard shortcuts using the defineKeymap function:
import { defineKeymap } from 'prosekit/core'

const extension = defineKeymap({
  'Mod-b': (state, dispatch) => {
    console.log('Bold shortcut pressed')
    return true
  },
  'Shift-Enter': (state, dispatch) => {
    console.log('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.

Modifiers

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
Use "Mod" instead of "Ctrl" to make your shortcuts work consistently across different operating systems.

Key Names

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 " ".

Examples

  • "Enter": ✅ Triggers when Enter is pressed
  • "Shift-Enter": ✅ Triggers when Shift+Enter is pressed
  • "Shift-enter": ❌ Invalid — "enter" is not a valid KeyboardEvent.key value
  • "Ctrl-a": ✅ Triggers when Ctrl+A is pressed
  • "Ctrl-A": ✅ Triggers when Ctrl+Shift+A 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 Shift+Space is pressed
  • "Shift-Space": ✅ Valid (equivalent to the above)

Common Keyboard Shortcuts

1

Text formatting shortcuts

import { defineKeymap, toggleMark } from 'prosekit/core'

const formattingKeymap = defineKeymap({
  'Mod-b': toggleMark({ type: 'bold' }),
  'Mod-i': toggleMark({ type: 'italic' }),
  'Mod-u': toggleMark({ type: 'underline' }),
  'Mod-Shift-x': toggleMark({ type: 'strike' }),
  'Mod-e': toggleMark({ type: 'code' }),
})
2

Heading shortcuts

import { defineKeymap, setBlockType } from 'prosekit/core'

const headingKeymap = defineKeymap({
  'Mod-Alt-1': setBlockType({ type: 'heading', attrs: { level: 1 } }),
  'Mod-Alt-2': setBlockType({ type: 'heading', attrs: { level: 2 } }),
  'Mod-Alt-3': setBlockType({ type: 'heading', attrs: { level: 3 } }),
  'Mod-Alt-0': setBlockType({ type: 'paragraph' }),
})
3

List shortcuts

import { defineKeymap, wrapInList } from 'prosekit/core'

const listKeymap = defineKeymap({
  'Mod-Shift-8': wrapInList({ listType: 'bulletList', itemType: 'listItem' }),
  'Mod-Shift-9': wrapInList({ listType: 'orderedList', itemType: 'listItem' }),
})
4

Custom command shortcuts

import { defineKeymap } from 'prosekit/core'

const customKeymap = defineKeymap({
  'Mod-k': (state, dispatch) => {
    // Open link dialog
    console.log('Insert link')
    return true
  },
  'Mod-Shift-c': (state, dispatch) => {
    // Insert code block
    console.log('Insert code block')
    return true
  },
})

Using useKeymap

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

React Example

import { useKeymap } from 'prosekit/react'
import { toggleMark } from 'prosekit/core'
import { useCallback } from 'react'

function Editor({ editor }) {
  const handleSave = useCallback((state, dispatch) => {
    // Custom save logic
    const json = editor.getDocJSON()
    localStorage.setItem('document', JSON.stringify(json))
    return true
  }, [editor])

  useKeymap({
    'Mod-s': handleSave,
    'Mod-b': toggleMark({ type: 'bold' }),
  }, { editor })

  return <div ref={editor.mount} />
}

Vue Example

import { useKeymap } from 'prosekit/vue'
import { toggleMark } from 'prosekit/core'

function setupEditor(editor) {
  const handleSave = (state, dispatch) => {
    const json = editor.getDocJSON()
    localStorage.setItem('document', JSON.stringify(json))
    return true
  }

  useKeymap({
    'Mod-s': handleSave,
    'Mod-b': toggleMark({ type: 'bold' }),
  }, { editor })
}

Extension Priority

Call defineKeymap multiple times to define additional shortcuts. Later definitions override earlier ones. Use withPriority to set extension priority:
import { Priority, withPriority, defineKeymap } from 'prosekit/core'

const extensionA = defineKeymap({
  'Mod-b': () => {
    console.log('Extension A')
    return true
  },
})

const extensionB = defineKeymap({
  'Mod-b': () => {
    console.log('Extension B')
    return true
  },
})

// Extension B will override Extension A
const extensionAPrioritized = withPriority(extensionA, Priority.high)
// Now Extension A has higher priority

Base Keymap

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. The base keymap includes:
  • Enter - Create a new paragraph
  • Mod-Enter - Exit current block
  • Backspace - Delete backward
  • Delete - Delete forward
  • Mod-z - Undo
  • Mod-Shift-z or Mod-y - Redo
  • Mod-a - Select all
  • Arrow keys for navigation

Complete Example

Here’s a complete example with custom keyboard shortcuts:
import { defineBasicExtension } from 'prosekit/basic'
import { 
  createEditor, 
  defineKeymap, 
  toggleMark,
  setBlockType,
  union 
} from 'prosekit/core'

// Define custom shortcuts
const customKeymap = defineKeymap({
  // Formatting
  'Mod-b': toggleMark({ type: 'bold' }),
  'Mod-i': toggleMark({ type: 'italic' }),
  'Mod-u': toggleMark({ type: 'underline' }),
  
  // Headings
  'Mod-Alt-1': setBlockType({ type: 'heading', attrs: { level: 1 } }),
  'Mod-Alt-2': setBlockType({ type: 'heading', attrs: { level: 2 } }),
  'Mod-Alt-3': setBlockType({ type: 'heading', attrs: { level: 3 } }),
  
  // Custom commands
  'Mod-s': (state, dispatch) => {
    // Save document
    console.log('Saving document...')
    return true
  },
  
  'Mod-k': (state, dispatch) => {
    // Open link dialog
    console.log('Opening link dialog...')
    return true
  },
  
  'Esc': (state, dispatch) => {
    // Clear selection or close dialog
    console.log('Escape pressed')
    return true
  },
})

// Combine with basic extension
const extension = union(
  defineBasicExtension(),
  customKeymap,
)

const editor = createEditor({ extension })

Platform-Specific Shortcuts

Create platform-specific shortcuts:
import { defineKeymap } from 'prosekit/core'

const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.platform)

const platformKeymap = defineKeymap({
  // Use different shortcuts for Mac and other platforms
  ...(isMac ? {
    'Cmd-d': () => {
      console.log('Mac duplicate line')
      return true
    },
  } : {
    'Ctrl-d': () => {
      console.log('Windows/Linux duplicate line')
      return true
    },
  }),
})

Troubleshooting

Shortcuts not working

If your shortcuts aren’t working:
  1. Verify the key name is correct (check KeyboardEvent.key values)
  2. Check for conflicts with browser shortcuts
  3. Ensure the editor has focus
  4. Verify the command function returns true when successful
  5. Check extension priority if shortcuts are being overridden

Browser shortcut conflicts

Some shortcuts may conflict with browser defaults:
  1. Ctrl-w / Cmd-w - Close tab (cannot be overridden)
  2. Ctrl-t / Cmd-t - New tab (cannot be overridden)
  3. Ctrl-n / Cmd-n - New window (cannot be overridden)
  4. F5 / Ctrl-r - Refresh (can be overridden but not recommended)
Avoid these shortcuts or inform users about the conflicts.

Shortcuts not working on specific elements

If shortcuts work in the editor but not in other elements:
  1. Ensure the element has focus
  2. Check if the element is inside the ProseKit editor context
  3. Verify event propagation isn’t being stopped

Next Steps

Build docs developers (and LLMs) love