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
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' }),
})
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' }),
})
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' }),
})
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 })
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:
- Verify the key name is correct (check KeyboardEvent.key values)
- Check for conflicts with browser shortcuts
- Ensure the editor has focus
- Verify the command function returns
true when successful
- Check extension priority if shortcuts are being overridden
Browser shortcut conflicts
Some shortcuts may conflict with browser defaults:
Ctrl-w / Cmd-w - Close tab (cannot be overridden)
Ctrl-t / Cmd-t - New tab (cannot be overridden)
Ctrl-n / Cmd-n - New window (cannot be overridden)
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:
- Ensure the element has focus
- Check if the element is inside the ProseKit editor context
- Verify event propagation isn’t being stopped
Next Steps