Link extension for tiptap rich text editor providing automatic link detection, paste handling, click behavior, and XSS protection.
88
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.
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',
}),
],
});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;
}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 selectionPaste 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 textAdvanced 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 contentSophisticated 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;
});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 linksContext-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)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 lifecycleThe 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 comparisonUX 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 systemRobust 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 dataEdge 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 protocolsThe 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/** 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-linkdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10