Skip to main content
Paste rules allow you to modify content when it’s pasted or dragged into the editor. They’re perfect for auto-linking URLs, converting markdown to rich text, or cleaning up pasted content.

Overview

ProseKit provides two types of paste rules:
  • Custom Paste Rules: Transform the entire pasted slice
  • Mark Paste Rules: Apply marks based on regex patterns
Paste rules run before the content is inserted into the document, giving you full control over the transformation.

Custom Paste Rules

Define custom logic to transform pasted content.
import { definePasteRule } from '@prosekit/extensions/paste-rule'

const extension = definePasteRule({
  handler: ({ slice, view, plain }) => {
    if (plain) {
      // Skip transformation for plain text paste (Shift+Paste)
      return slice
    }
    
    // Transform the slice
    // ...
    
    return transformedSlice
  },
})

Handler Options

slice
Slice
The content being pasted as a ProseMirror Slice.
view
EditorView
The current editor view.
plain
boolean
Whether the content is being pasted as plain text (true when Shift key is held).

Mark Paste Rules

Automatically apply marks to pasted text that matches a regex pattern.

Basic Usage

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const autoLinkExtension = defineMarkPasteRule({
  regex: /(https?:\/\/[^\s]+)/g,
  type: 'link',
  getAttrs: (match) => ({
    href: match[1],
  }),
})
The regex pattern must have the g flag to match all instances in the pasted content.

With Conditional Attributes

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const smartLinkExtension = defineMarkPasteRule({
  regex: /(https?:\/\/[^\s]+)/g,
  type: 'link',
  getAttrs: (match) => {
    const url = match[1]
    
    // Only link if it's a valid URL
    try {
      new URL(url)
      return { href: url }
    } catch {
      return false // Don't apply the mark
    }
  },
})

Custom Skip Logic

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const highlightExtension = defineMarkPasteRule({
  regex: /==(.*?)==/g,
  type: 'highlight',
  shouldSkip: (node) => {
    // Don't apply in code blocks or if already highlighted
    return (
      node.type.spec.code ||
      node.marks.some((mark) => mark.type.name === 'highlight')
    )
  },
})

API Reference

defineMarkPasteRule

regex
RegExp
required
The regular expression to match against. Must have the g flag to match all instances.
type
string | MarkType
required
The mark type to apply to matched text.
getAttrs
(match: RegExpExecArray) => Attrs | null | undefined | false
Function to compute attributes for the mark. Return false to skip applying the mark for this match.Default: null (empty attributes)
shouldSkip
(node: ProseMirrorNode) => boolean
Function to determine if a text node should be skipped.Default: Skip code nodes and nodes that already have the target mark.

Real-World Examples

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const autoLinkExtension = defineMarkPasteRule({
  regex: /(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))/g,
  type: 'link',
  getAttrs: (match) => ({
    href: match[1],
  }),
})
import { definePasteRule } from '@prosekit/extensions/paste-rule'
import { Fragment, Slice } from '@prosekit/pm/model'

const markdownLinkExtension = definePasteRule({
  handler: ({ slice, view, plain }) => {
    if (plain) return slice
    
    // Transform [text](url) to link marks
    const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g
    
    // Process slice content...
    // This is a simplified example
    
    return transformedSlice
  },
})

Clean HTML on Paste

import { definePasteRule } from '@prosekit/extensions/paste-rule'

const cleanHtmlExtension = definePasteRule({
  handler: ({ slice, view, plain }) => {
    if (plain) return slice
    
    // Remove unwanted attributes or nodes
    // For example, strip inline styles
    
    const cleanedSlice = removeInlineStyles(slice)
    return cleanedSlice
  },
})

function removeInlineStyles(slice: Slice): Slice {
  // Implementation to clean the slice
  // ...
  return slice
}

Email Address Detection

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const emailLinkExtension = defineMarkPasteRule({
  regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
  type: 'link',
  getAttrs: (match) => ({
    href: `mailto:${match[1]}`,
  }),
})

Markdown Bold to Rich Text

import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const markdownBoldExtension = defineMarkPasteRule({
  regex: /\*\*([^*]+)\*\*/g,
  type: 'bold',
  getAttrs: (match) => {
    // Return null to use default attributes
    return null
  },
})

Combining Rules

import { union } from '@prosekit/core'
import { defineMarkPasteRule } from '@prosekit/extensions/paste-rule'

const pasteExtensions = union([
  // Auto-link URLs
  defineMarkPasteRule({
    regex: /(https?:\/\/[^\s]+)/g,
    type: 'link',
    getAttrs: (match) => ({ href: match[1] }),
  }),
  
  // Auto-link emails
  defineMarkPasteRule({
    regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
    type: 'link',
    getAttrs: (match) => ({ href: `mailto:${match[1]}` }),
  }),
  
  // Convert markdown bold
  defineMarkPasteRule({
    regex: /\*\*([^*]+)\*\*/g,
    type: 'bold',
  }),
  
  // Convert markdown italic
  defineMarkPasteRule({
    regex: /\*([^*]+)\*/g,
    type: 'italic',
  }),
])

Use Cases

Auto-Linking

Automatically convert URLs and email addresses to clickable links when pasted.

Markdown Conversion

Convert markdown syntax to rich text formatting on paste.

Content Cleaning

Strip unwanted formatting, styles, or attributes from pasted content.

Smart Formatting

Apply intelligent formatting based on the pasted content’s patterns.

Tips

Check the plain parameter to skip transformations when users paste with Shift (paste as plain text).
Mark paste rules are more efficient than custom paste rules when you only need to apply marks based on patterns.
Be careful with aggressive paste rules that might remove content users want to keep. Consider providing a way to undo paste transformations.