Mark rules automatically apply marks to text that matches a pattern and remove them when the text no longer matches. Unlike input rules or paste rules, mark rules continuously monitor the document and update marks as content changes.
Overview
Mark rules are perfect for:
Syntax highlighting : Highlight code syntax, mentions, or hashtags
Real-time validation : Mark invalid input like broken links or incorrect formats
Live formatting : Apply formatting based on content patterns
The marks are applied and removed automatically as users type, paste, or modify content.
Basic Usage
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const mentionRule = defineMarkRule ({
regex: /@ ( \w + ) / g ,
type: 'mention' ,
attrs : ( match ) => ({
username: match [ 1 ],
}),
})
This will automatically apply a mention mark to any text matching @username and remove it when the text changes.
API Reference
The regular expression to match against. Must have the g flag to match all instances in the document.
type
string | MarkType
required
The mark type to apply to matched text.
attrs
Attrs | null | ((match: RegExpMatchArray) => Attrs | null)
Attributes to set on the mark. Can be a static object or a function that receives the regex match. Default: null (empty attributes)
The regex must include the g flag to match all occurrences in the document.
Real-World Examples
Mention Detection
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const mentionExtension = defineMarkRule ({
regex: /@ ( [ a-zA-Z0-9_ ] + ) / g ,
type: 'mention' ,
attrs : ( match ) => ({
username: match [ 1 ],
}),
})
Automatically marks mentions like @john or @sarah_smith as you type.
Hashtag Highlighting
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const hashtagExtension = defineMarkRule ({
regex: /# ( [ a-zA-Z0-9_ ] + ) / g ,
type: 'hashtag' ,
attrs : ( match ) => ({
tag: match [ 1 ],
}),
})
URL Detection
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const urlExtension = defineMarkRule ({
regex: / ( https ? : \/\/ (?: www \. ) ? [ -a-zA-Z0-9@:%._ \+ ~#= ] {1,256} \. [ a-zA-Z0-9() ] {1,6} \b (?: [ -a-zA-Z0-9()@:%_ \+ .~#?& \/ = ] * )) / g ,
type: 'link' ,
attrs : ( match ) => ({
href: match [ 1 ],
}),
})
Automatically links URLs as they’re typed.
Email Address Detection
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const emailExtension = defineMarkRule ({
regex: / ( [ a-zA-Z0-9._%+- ] + @ [ a-zA-Z0-9.- ] + \. [ a-zA-Z ] {2,} ) / g ,
type: 'link' ,
attrs : ( match ) => ({
href: `mailto: ${ match [ 1 ] } ` ,
}),
})
Syntax Highlighting
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
import { union } from '@prosekit/core'
const syntaxHighlighting = union ([
// Highlight keywords
defineMarkRule ({
regex: / \b ( function | const | let | var | return | if | else ) \b / g ,
type: 'syntax' ,
attrs : () => ({ class: 'keyword' }),
}),
// Highlight strings
defineMarkRule ({
regex: / ( [ '"` ] )(?:(?=( \\ ? )) \2 . ) *? \1 / g ,
type: 'syntax' ,
attrs : () => ({ class: 'string' }),
}),
// Highlight numbers
defineMarkRule ({
regex: / \b \d + \b / g ,
type: 'syntax' ,
attrs : () => ({ class: 'number' }),
}),
])
Price Detection
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const priceExtension = defineMarkRule ({
regex: / \$ ( [ 0-9, ] + (?: \. [ 0-9 ] {2} ) ? ) / g ,
type: 'price' ,
attrs : ( match ) => {
const amount = match [ 1 ]. replace ( /,/ g , '' )
return {
amount: parseFloat ( amount ),
currency: 'USD' ,
}
},
})
Phone Number Detection
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const phoneExtension = defineMarkRule ({
regex: / \b ( \+ ? 1 ? [ -. ] ? ) ? \( ? ( [ 0-9 ] {3} ) \) ? [ -. ] ? ( [ 0-9 ] {3} ) [ -. ] ? ( [ 0-9 ] {4} ) \b / g ,
type: 'phone' ,
attrs : ( match ) => ({
number: match [ 0 ]. replace ( / [ ^ 0-9+ ] / g , '' ),
}),
})
Multiple Rules
Combine multiple mark rules to handle different patterns:
import { union } from '@prosekit/core'
import { defineMarkRule } from '@prosekit/extensions/mark-rule'
const smartMarkings = union ([
// Mentions
defineMarkRule ({
regex: /@ ( [ a-zA-Z0-9_ ] + ) / g ,
type: 'mention' ,
attrs : ( match ) => ({ username: match [ 1 ] }),
}),
// Hashtags
defineMarkRule ({
regex: /# ( [ a-zA-Z0-9_ ] + ) / g ,
type: 'hashtag' ,
attrs : ( match ) => ({ tag: match [ 1 ] }),
}),
// URLs
defineMarkRule ({
regex: / ( https ? : \/\/ [ ^ \s ] + ) / g ,
type: 'link' ,
attrs : ( match ) => ({ href: match [ 1 ] }),
}),
// Emails
defineMarkRule ({
regex: / ( [ a-zA-Z0-9._%+- ] + @ [ a-zA-Z0-9.- ] + \. [ a-zA-Z ] {2,} ) / g ,
type: 'link' ,
attrs : ( match ) => ({ href: `mailto: ${ match [ 1 ] } ` }),
}),
])
Mark rules run on every document change, so consider:
Keep regex patterns efficient. Complex patterns can slow down the editor on large documents.
Consider limiting mark rules to specific node types if they don’t need to apply everywhere.
Test performance with large documents to ensure mark rules don’t cause lag.
Use Cases
Mentions & Tags Automatically detect and format @mentions and #hashtags as users type.
Auto-Linking Convert URLs and email addresses to links in real-time.
Syntax Highlighting Highlight keywords, strings, and other syntax elements in code.
Data Detection Detect and mark special data like prices, dates, or phone numbers.
Comparison with Other Rules
Feature Input Rule Paste Rule Mark Rule Triggered by Typing Pasting Any change Timing On pattern match On paste Continuous Removal Manual Manual Automatic Best for Shortcuts Paste transforms Live formatting
Use input rules for one-time transformations, paste rules for paste-specific handling, and mark rules for continuous live formatting.