CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tiptap--extension-link

Link extension for tiptap rich text editor providing automatic link detection, paste handling, click behavior, and XSS protection.

88

1.23x
Overview
Eval results
Files

paste-processing.mddocs/

Paste Processing

Intelligent URL paste handling that automatically converts pasted URLs into clickable links. The paste handler detects when users paste URL-only content and automatically applies link formatting.

Capabilities

Paste Handler Plugin Function

Creates a ProseMirror plugin that processes paste events and converts URLs to links.

/**
 * Creates a ProseMirror plugin for handling URL paste operations
 * @param options - Configuration options for paste behavior
 * @returns ProseMirror Plugin instance
 */
function pasteHandler(options: PasteHandlerOptions): Plugin;

Usage Examples:

import { Plugin } from "@tiptap/pm/state";
import { pasteHandler } from "@tiptap/extension-link";

// Create paste handler plugin
const linkPastePlugin = pasteHandler({
  editor: editorInstance,
  defaultProtocol: 'https',
  type: linkMarkType,
});

// Plugin is automatically used by Link extension
const editor = new Editor({
  extensions: [
    Link.configure({
      linkOnPaste: true, // Enables the paste handler plugin
      defaultProtocol: 'https',
    }),
  ],
});

Paste Handler Options Interface

Configuration interface for paste handler plugin behavior and settings.

interface PasteHandlerOptions {
  /**
   * Tiptap editor instance for command access
   */
  editor: Editor;

  /**
   * Default protocol to use for protocol-less URLs
   * @example 'https'
   */
  defaultProtocol: string;

  /**
   * The link mark type from ProseMirror schema
   */
  type: MarkType;
}

Paste Detection Logic

The paste handler implements intelligent URL detection from pasted content.

Detection Process:

// 1. Extract text content from paste slice
// 2. Check if selection is not empty (has content to replace)
// 3. Analyze if pasted content is pure URL
// 4. Validate URL using linkifyjs
// 5. Apply link mark to selection

Paste Requirements:

// Paste handler activates when:
// 1. Selection is not empty (text is selected)
// 2. Pasted content is purely a URL (no additional text)
// 3. URL passes linkifyjs validation
// 4. URL matches exactly with pasted text content

// Does NOT activate when:
// - Nothing is selected (empty selection)
// - Pasted content contains mixed text and URLs
// - Pasted content is not a valid URL
// - URL doesn't match the complete pasted text

URL Validation and Processing

Advanced URL validation using linkifyjs integration for reliable link detection.

/**
 * URL validation process used by paste handler
 * Uses linkifyjs.find() with defaultProtocol configuration
 */
interface URLValidation {
  /** Find URLs in text using linkifyjs */
  findUrls: (text: string, options: { defaultProtocol: string }) => LinkMatch[];
  
  /** Validate that URL matches complete pasted text */
  exactMatch: (url: LinkMatch, pastedText: string) => boolean;
  
  /** Extract href for link creation */
  extractHref: (url: LinkMatch) => string;
}

interface LinkMatch {
  isLink: boolean;
  value: string;
  href: string;
}

Validation Examples:

// Valid paste scenarios:
'https://tiptap.dev' // ✓ Complete URL
'http://example.com/path' // ✓ Complete URL with path
'ftp://files.example.com' // ✓ Custom protocol (if configured)
'www.example.com' // ✓ Domain without protocol (adds defaultProtocol)

// Invalid paste scenarios:
'Visit https://tiptap.dev' // ✗ Mixed text and URL
'https://tiptap.dev and more' // ✗ URL with additional text
'not a url' // ✗ Not a valid URL
'' // ✗ Empty content

Text Content Extraction

Sophisticated text extraction from ProseMirror paste slices.

/**
 * Text extraction from paste slice
 * Handles complex ProseMirror document structures
 */
interface TextExtraction {
  /** Extract plain text from all nodes in slice */
  extractFromSlice: (slice: Slice) => string;
  
  /** Handle different node types */
  nodeTraversal: (node: Node) => string;
  
  /** Concatenate text content */
  textConcatenation: string;
}

Extraction Process:

// Text extraction handles:
// 1. Multiple nodes in paste slice
// 2. Nested node structures
// 3. Text node content extraction
// 4. Content concatenation
// 5. Whitespace preservation

let textContent = '';
slice.content.forEach(node => {
  textContent += node.textContent;
});

Advanced Paste Configurations

Complex paste handling configurations for different use cases.

Protocol-Aware Pasting:

import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";

const editor = new Editor({
  extensions: [
    Link.configure({
      linkOnPaste: true,
      defaultProtocol: 'https', // Adds https:// to protocol-less URLs
      protocols: ['ftp', 'ssh'], // Support additional protocols
    }),
  ],
});

// Paste behavior:
// 'example.com' → 'https://example.com'
// 'ftp://files.com' → 'ftp://files.com' (preserved)

Conditional Paste Processing:

const editor = new Editor({
  extensions: [
    Link.configure({
      linkOnPaste: true,
      isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
        // Custom paste validation
        const allowedDomains = ['example.com', 'tiptap.dev'];
        
        try {
          const urlObj = new URL(url);
          return allowedDomains.includes(urlObj.hostname);
        } catch {
          return false;
        }
      },
    }),
  ],
});

// Only URLs from allowed domains will be converted to links

Context-Sensitive Pasting:

// The paste handler respects editor context:

// In regular paragraphs:
editor.commands.insertContent('paragraph text');
editor.commands.selectAll();
// Paste 'https://example.com' → converts to link

// In code blocks:
editor.commands.setCodeBlock();
editor.commands.insertContent('code content');
editor.commands.selectAll();
// Paste 'https://example.com' → remains as plain text (if configured)

Integration with Link Extension

The paste handler plugin is automatically configured and managed by the Link extension.

Automatic Integration:

// When linkOnPaste is enabled in Link configuration:
const editor = new Editor({
  extensions: [
    Link.configure({
      linkOnPaste: true, // Automatically adds paste handler plugin
      defaultProtocol: 'https',
    }),
  ],
});

// The Link extension automatically:
// 1. Creates paste handler plugin with proper configuration
// 2. Passes editor reference and mark type
// 3. Configures default protocol
// 4. Manages plugin lifecycle

Performance Optimization

The paste handler is optimized for performance and user experience.

Performance Features:

// 1. Early exit for empty selections
// 2. Efficient text content extraction
// 3. Single linkifyjs validation call
// 4. Minimal DOM manipulation
// 5. Fast exact match comparison

UX Considerations:

// 1. Only processes selected text (clear user intent)
// 2. Preserves original text if URL is invalid
// 3. Seamless link creation for valid URLs
// 4. Respects existing link marks
// 5. Works with undo/redo system

Error Handling

Robust error handling for various paste scenarios and edge cases.

Error Prevention:

// 1. Graceful handling of invalid URLs
// 2. Safe text extraction from complex slices
// 3. Fallback to original paste behavior
// 4. Validation of linkifyjs results
// 5. Protection against malformed paste data

Edge Case Handling:

// Handles edge cases:
// - Empty paste content
// - Binary/non-text paste data
// - Malformed URLs
// - Very long URLs
// - Unicode characters in URLs
// - Protocol-less URLs with custom protocols

Command Integration

The paste handler integrates with Tiptap's command system for consistent behavior.

Command Usage:

// The paste handler uses editor.commands.setMark() internally:
return options.editor.commands.setMark(options.type, {
  href: link.href,
});

// This ensures:
// 1. Proper mark application
// 2. Transaction creation
// 3. Undo/redo support
// 4. Event emission
// 5. Plugin coordination

Types

/** Configuration options for paste handler plugin */
interface PasteHandlerOptions {
  editor: Editor;
  defaultProtocol: string;
  type: MarkType;
}

/** Link match result from linkifyjs */
interface LinkMatch {
  isLink: boolean;
  value: string;
  href: string;
}

/** ProseMirror slice for paste content */
interface Slice {
  content: Fragment;
  openStart: number;
  openEnd: number;
}

Install with Tessl CLI

npx tessl i tessl/npm-tiptap--extension-link

docs

autolink.md

click-handling.md

index.md

link-commands.md

link-configuration.md

paste-processing.md

url-validation.md

tile.json