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.
Overview
The priority system in ProseKit allows you to control the order in which extensions are applied. This is useful when you need certain extensions to take precedence over others.
Priority Levels
ProseKit provides five priority levels:
const Priority = {
lowest: 0,
low: 1,
default: 2,
high: 3,
highest: 4,
} as const
Lowest priority - applied first, overridden by all other priorities
Low priority - applied after lowest, before default
Default priority - used when no priority is specified
High priority - applied after default, before highest
Highest priority - applied last, overrides all other priorities
withPriority()
Returns a new extension with the specified priority.
Function Signature
function withPriority<T extends Extension>(
extension: T,
priority: Priority
): T
Parameters
The extension to apply priority to
The priority level to assign
Return Value
A new extension with the specified priority. The type is preserved.
Examples
Basic Usage
import { withPriority, Priority } from 'prosekit/core'
import { defineMyExtension } from './my-extension'
const highPriorityExtension = withPriority(
defineMyExtension(),
Priority.high
)
Overriding Default Behavior
Use priority to ensure your custom extension takes precedence over built-in extensions:
import { createEditor, union, withPriority, Priority } from 'prosekit/core'
import { defineKeymap } from 'prosekit/core'
import { defineBasicExtension } from 'prosekit/basic'
// Custom keymap that should override default shortcuts
const customKeymap = withPriority(
defineKeymap({
'Mod-b': () => {
console.log('Custom bold behavior')
return true
}
}),
Priority.high
)
const editor = createEditor({
extension: union([
defineBasicExtension(), // includes default Mod-b handler
customKeymap // will override the default
])
})
Plugin Execution Order
Control when plugins run relative to each other:
import { union, withPriority, Priority } from 'prosekit/core'
import { definePlugin } from 'prosekit/core'
// This plugin should run first
const initPlugin = withPriority(
definePlugin(() => {
return {
view(editorView) {
console.log('Initialize first')
return {}
}
}
}),
Priority.highest
)
// This plugin should run last
const cleanupPlugin = withPriority(
definePlugin(() => {
return {
view(editorView) {
console.log('Initialize last')
return {}
}
}
}),
Priority.lowest
)
const extension = union([initPlugin, cleanupPlugin])
Schema Customization
Ensure custom node/mark specs override defaults:
import { union, withPriority, Priority } from 'prosekit/core'
import { defineNodeSpec } from 'prosekit/core'
import { defineParagraph } from 'prosekit/extensions/node'
// Custom paragraph implementation
const customParagraph = withPriority(
defineNodeSpec({
name: 'paragraph',
content: 'inline*',
group: 'block',
parseDOM: [{ tag: 'p.custom' }],
toDOM: () => ['p', { class: 'custom' }, 0]
}),
Priority.high
)
const extension = union([
defineParagraph(), // default paragraph
customParagraph // will override the default
])
Event Handler Priority
Control the order of event handlers:
import { withPriority, Priority, defineKeyDownHandler } from 'prosekit/core'
// Critical handler that should run first
const criticalHandler = withPriority(
defineKeyDownHandler((view, event) => {
if (event.key === 'Escape') {
console.log('Critical escape handler')
return true
}
return false
}),
Priority.highest
)
// Optional handler that runs later
const optionalHandler = withPriority(
defineKeyDownHandler((view, event) => {
console.log('Optional handler')
return false
}),
Priority.low
)
Conditional Priority
Dynamically set priority based on configuration:
import { withPriority, Priority } from 'prosekit/core'
import { defineMyExtension } from './my-extension'
interface Config {
overrideDefaults?: boolean
}
function createExtension(config: Config) {
const extension = defineMyExtension()
if (config.overrideDefaults) {
return withPriority(extension, Priority.highest)
}
return extension
}
// High priority version
const ext1 = createExtension({ overrideDefaults: true })
// Default priority version
const ext2 = createExtension({ overrideDefaults: false })
How Priority Works
Extension Processing Order
When multiple extensions are combined with union, they are processed based on priority:
- Extensions are sorted by priority (lowest to highest)
- Higher priority extensions are applied last
- Later applications can override earlier ones
Plugin Order
For ProseMirror plugins:
- Higher priority plugins are added later in the plugin array
- Later plugins in the array receive events first
- This allows high-priority plugins to intercept and prevent event handling by low-priority plugins
Schema Merging
For node and mark specifications:
- Higher priority specs override lower priority specs with the same name
- Parse rules from higher priority extensions take precedence
- This allows customization of built-in node/mark behavior
Best Practices
Use Sparingly
Only use priority when necessary. Most extensions work fine with default priority:
// Good - no priority needed
const extension = union([
defineDoc(),
defineParagraph(),
defineBold()
])
// Only when necessary
const extension = union([
defineDoc(),
defineParagraph(),
withPriority(customBold(), Priority.high)
])
Document Priority Decisions
When using priority, document why:
// Good - documents the reason
const customKeymap = withPriority(
defineKeymap({ /* ... */ }),
Priority.high // Override default shortcuts
)
// Not clear why priority is needed
const customKeymap = withPriority(
defineKeymap({ /* ... */ }),
Priority.high
)
Test Priority Interactions
When using multiple extensions with different priorities, test their interactions:
import { test } from 'vitest'
import { createEditor, union, withPriority, Priority } from 'prosekit/core'
test('high priority handler runs first', () => {
let order: string[] = []
const lowHandler = withPriority(
defineKeyDownHandler(() => {
order.push('low')
return false
}),
Priority.low
)
const highHandler = withPriority(
defineKeyDownHandler(() => {
order.push('high')
return false
}),
Priority.high
)
const editor = createEditor({
extension: union([lowHandler, highHandler])
})
// Simulate event
// ...
expect(order).toEqual(['high', 'low'])
})
Common Use Cases
Custom Keyboard Shortcuts
Override default shortcuts with high priority
Plugin Initialization
Ensure setup plugins run before feature plugins
Schema Customization
Replace built-in node/mark implementations
Event Interception
Handle events before they reach default handlers
See Also