Skip to main content
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

regex
RegExp
required
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]}` }),
  }),
])

Performance Considerations

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

FeatureInput RulePaste RuleMark Rule
Triggered byTypingPastingAny change
TimingOn pattern matchOn pasteContinuous
RemovalManualManualAutomatic
Best forShortcutsPaste transformsLive formatting
Use input rules for one-time transformations, paste rules for paste-specific handling, and mark rules for continuous live formatting.