> ## 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

> Learn how to add and customize keyboard shortcuts in your ProseKit editor

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:

```typescript theme={null}
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](https://prosemirror.net/docs/ref/#state.Command) functions to execute.

Key names are strings like `Shift-Ctrl-Enter`, consisting of zero or more **modifiers** followed by a **[KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)** 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

<Tip>
  Use `"Mod"` instead of `"Ctrl"` to make your shortcuts work consistently across different operating systems.
</Tip>

## Key Names

A **[KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)** 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

<Steps>
  <Step title="Text formatting shortcuts">
    ```typescript theme={null}
    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' }),
    })
    ```
  </Step>

  <Step title="Heading shortcuts">
    ```typescript theme={null}
    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' }),
    })
    ```
  </Step>

  <Step title="List shortcuts">
    ```typescript theme={null}
    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' }),
    })
    ```
  </Step>

  <Step title="Custom command shortcuts">
    ```typescript theme={null}
    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
      },
    })
    ```
  </Step>
</Steps>

## Using `useKeymap`

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

### React Example

```typescript theme={null}
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

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) 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

* [Custom Extensions](/guides/custom-extensions) - Build custom editor functionality
* [Saving and Loading](/guides/saving-loading) - Persist editor content
* [Collaboration](/guides/collaboration) - Add real-time collaboration
