Link extension for tiptap rich text editor providing automatic link detection, paste handling, click behavior, and XSS protection.
88
Click event processing for link navigation with configurable behavior and selection options. The click handler provides intelligent link navigation while respecting editor state and user preferences.
Creates a ProseMirror plugin that handles click events on links within the editor.
/**
* Creates a ProseMirror plugin for handling link clicks
* @param options - Configuration options for click behavior
* @returns ProseMirror Plugin instance
*/
function clickHandler(options: ClickHandlerOptions): Plugin;Usage Examples:
import { Plugin } from "@tiptap/pm/state";
import { clickHandler } from "@tiptap/extension-link";
// Create click handler plugin
const linkClickPlugin = clickHandler({
type: linkMarkType,
editor: editorInstance,
enableClickSelection: true,
});
// Plugin is automatically used by Link extension
const editor = new Editor({
extensions: [
Link.configure({
openOnClick: true, // Enables the click handler plugin
enableClickSelection: false,
}),
],
});Configuration interface for click handler plugin behavior and features.
interface ClickHandlerOptions {
/**
* The link mark type from ProseMirror schema
*/
type: MarkType;
/**
* Tiptap editor instance for command access
*/
editor: Editor;
/**
* If enabled, clicking a link will select the entire link text
* @default false
*/
enableClickSelection?: boolean;
}The click handler implements intelligent behavior based on editor state and link attributes.
Click Processing Flow:
// 1. Event validation (left-click only)
// 2. Editor editability check
// 3. Link element detection (direct or ancestor)
// 4. Attribute extraction (href, target, rel)
// 5. Optional link selection
// 6. Link navigationEvent Filtering:
// Only processes:
// - Left mouse button (button === 0)
// - When editor is editable
// - When click target is a link or contains a link
// - When link has valid href attribute
// Ignores:
// - Right-click and middle-click
// - Clicks when editor is not editable
// - Clicks on non-link elements
// - Links without href attributesAdvanced link element detection that handles various DOM structures.
/**
* Link detection algorithm used by click handler
* Finds link elements in click event target hierarchy
*/
interface LinkDetection {
// Direct link element
target: HTMLAnchorElement;
// Nested link detection
parentTraversal: HTMLElement[];
// Link identification
isLinkElement: (element: HTMLElement) => element is HTMLAnchorElement;
}Detection Examples:
// Direct link click
<a href="https://example.com">Click me</a>
// ✓ Detected: event.target is HTMLAnchorElement
// Nested element click
<a href="https://example.com">
<span>Click me</span>
</a>
// ✓ Detected: traverses parent elements to find <a>
// Complex nesting
<a href="https://example.com">
<strong>
<em>Click me</em>
</strong>
</a>
// ✓ Detected: traverses multiple parent levelsOptional link text selection when enableClickSelection is enabled.
/**
* Link selection behavior when enableClickSelection is true
* Selects the entire link text range before opening
*/
interface LinkSelection {
/** Extend mark range to select entire link */
extendMarkRange: (markName: string) => void;
/** Selection happens before link navigation */
selectionTiming: 'before-navigation';
}Usage Examples:
import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";
// Enable link selection on click
const editor = new Editor({
extensions: [
Link.configure({
openOnClick: true,
enableClickSelection: true, // Selects link text before opening
}),
],
});
// Manual link selection
editor.commands.extendMarkRange('link'); // Selects current linkConfigurable link opening behavior with target and security attributes.
Navigation Logic:
// 1. Extract link attributes from DOM or ProseMirror state
// 2. Priority: DOM attributes > ProseMirror mark attributes
// 3. Open link using window.open with proper target
// 4. Respect security attributes (rel="noopener")Attribute Sources:
// DOM attributes (higher priority)
const href = linkElement.href;
const target = linkElement.target;
// ProseMirror mark attributes (fallback)
const attrs = getAttributes(view.state, 'link');
const href = attrs.href;
const target = attrs.target;Complex click handling configurations for different use cases.
Conditional Link Opening:
import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";
const editor = new Editor({
extensions: [
Link.configure({
openOnClick: true,
enableClickSelection: false,
// Custom validation through isAllowedUri affects click behavior
isAllowedUri: (url) => {
// Only allow opening of safe URLs
const safeDomains = ['example.com', 'tiptap.dev'];
try {
const urlObj = new URL(url);
return safeDomains.includes(urlObj.hostname);
} catch {
return false;
}
},
}),
],
});Editor State-Aware Clicking:
// The click handler automatically respects editor state:
// When editor is not editable:
editor.setEditable(false);
// Links still clickable (navigation only)
// When editor is editable:
editor.setEditable(true);
// Links clickable with optional selectionCustom Click Handling:
// For custom click behavior, you can disable the built-in handler
// and implement your own:
const editor = new Editor({
extensions: [
Link.configure({
openOnClick: false, // Disable built-in click handling
}),
],
});
// Add custom click listener
editor.view.dom.addEventListener('click', (event) => {
const target = event.target as HTMLElement;
const link = target.closest('a');
if (link) {
event.preventDefault();
// Custom logic here
console.log('Link clicked:', link.href);
// Optional: use editor commands
if (confirmNavigation(link.href)) {
window.open(link.href, '_blank');
}
}
});The click handler includes security features to prevent malicious link behavior.
Security Features:
// 1. URL validation using configured isAllowedUri function
// 2. Proper target attribute handling
// 3. Security attributes preservation (rel="noopener")
// 4. XSS prevention through validation
// 5. Event sanitizationSafe Link Opening:
// The click handler ensures safe link opening:
window.open(href, target || '_blank');
// With security attributes:
// rel="noopener noreferrer" prevents window.opener access
// target="_blank" opens in new tab/windowThe click handler plugin is automatically configured and managed by the Link extension.
Automatic Integration:
// When openOnClick is enabled in Link configuration:
const editor = new Editor({
extensions: [
Link.configure({
openOnClick: true, // Automatically adds click handler plugin
enableClickSelection: true,
}),
],
});
// The Link extension automatically:
// 1. Creates click handler plugin with proper configuration
// 2. Passes editor reference and mark type
// 3. Configures selection behavior
// 4. Manages plugin lifecycleThe click handler is optimized for performance and user experience.
Performance Features:
// 1. Efficient event filtering (left-click only)
// 2. Fast DOM traversal for link detection
// 3. Minimal attribute extraction
// 4. No unnecessary selection operations
// 5. Early returns for invalid statesUX Considerations:
// 1. Respects editor editability state
// 2. Optional link text selection for visual feedback
// 3. Proper cursor behavior after navigation
// 4. Security-conscious link opening
// 5. Consistent behavior across different link structures/** Configuration options for click handler plugin */
interface ClickHandlerOptions {
type: MarkType;
editor: Editor;
enableClickSelection?: boolean;
}
/** Link element detection interface */
interface LinkElement extends HTMLAnchorElement {
href: string;
target?: string;
rel?: string;
}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