Skip to main content

Overview

The Link extension enables hyperlink support in your editor with automatic URL detection, paste handling, and interactive link creation. It automatically detects and converts URLs as you type or paste them.

Installation

import { defineLink } from '@prosekit/extensions'

Basic Usage

import { defineLink } from '@prosekit/extensions'
import { createEditor } from '@prosekit/core'

const editor = createEditor({
  extensions: [
    defineLink(),
    // ... other extensions
  ],
})
Links support standard HTML anchor attributes:
interface LinkAttrs {
  /**
   * The URL the link points to.
   */
  href: string
  
  /**
   * Where to open the link (e.g., '_blank', '_self').
   */
  target?: string | null
  
  /**
   * The relationship between documents (e.g., 'noopener', 'noreferrer').
   */
  rel?: string | null
}

Commands

Applies a link mark to the current selection.
editor.commands.addLink({
  href: 'https://example.com',
  target: '_blank',
  rel: 'noopener noreferrer',
})
Parameters:
  • href: The URL (required)
  • target: Where to open the link (optional)
  • rel: Link relationship attributes (optional)
Removes the link mark from the current selection.
editor.commands.removeLink()
Toggles a link on or off. If a link exists, it removes it. Otherwise, it adds a new link.
editor.commands.toggleLink({
  href: 'https://example.com',
  target: '_blank',
})
Expands the selection to encompass the entire link.
editor.commands.expandLink()

Automatic URL Detection

The Link extension includes several automatic detection features:

Input Rule

Automatically converts URLs when you type a space after them:
https://example.com [space]
This will automatically create a link mark for the URL.

Enter Rule

Automatically converts URLs when you press Enter:
https://example.com [enter]

Mark Rule

Automatically applies and removes link marks as you type, providing real-time feedback.

Paste Rule

Automatically converts pasted URLs into clickable links:
import { defineLinkPasteRule } from '@prosekit/extensions'

Usage Examples

// Add a simple link
editor.commands.addLink({ href: 'https://example.com' })

// Add a link that opens in a new tab
editor.commands.addLink({
  href: 'https://example.com',
  target: '_blank',
  rel: 'noopener noreferrer',
})

// Toggle link on selected text
editor.commands.toggleLink({ href: 'https://example.com' })

// Remove link from selection
editor.commands.removeLink()
// Expand selection to full link
editor.commands.expandLink()

// Update the href
editor.commands.addLink({
  href: 'https://newurl.com',
})
// Example: Prompt user for URL and create link
const url = prompt('Enter URL:')
if (url) {
  editor.commands.addLink({
    href: url,
    target: '_blank',
    rel: 'noopener noreferrer',
  })
}

Complete Example

import { createEditor } from '@prosekit/core'
import { defineLink } from '@prosekit/extensions'

const editor = createEditor({
  extensions: [defineLink()],
})

// Select some text
editor.commands.setTextSelection({ from: 1, to: 10 })

// Add a link to the selected text
editor.commands.addLink({
  href: 'https://example.com',
  target: '_blank',
})

// Later, expand to full link and remove it
editor.commands.expandLink()
editor.commands.removeLink()
The extension detects various URL formats:
  • http://example.com
  • https://example.com
  • www.example.com
  • example.com (in some contexts)

Accessibility

For external links, it’s recommended to include appropriate rel attributes:
editor.commands.addLink({
  href: 'https://external-site.com',
  target: '_blank',
  rel: 'noopener noreferrer', // Security best practice
})
  • noopener: Prevents the new page from accessing window.opener
  • noreferrer: Prevents passing referrer information
  • nofollow: Indicates the link should not be followed by search engines (for user-generated content)
Links render as standard HTML anchor elements and can be styled with CSS:
.ProseMirror a {
  color: #0066cc;
  text-decoration: underline;
  cursor: pointer;
}

.ProseMirror a:hover {
  color: #004499;
}

.ProseMirror a:visited {
  color: #551a8b;
}

Advanced Usage

You can implement custom link click handlers:
editor.view.dom.addEventListener('click', (event) => {
  const target = event.target as HTMLElement
  if (target.tagName === 'A') {
    event.preventDefault()
    const href = target.getAttribute('href')
    // Custom handling, e.g., open in modal or custom navigation
    console.log('Link clicked:', href)
  }
})

Validating URLs

function isValidUrl(url: string): boolean {
  try {
    new URL(url)
    return true
  } catch {
    return false
  }
}

// Use before adding link
const url = 'https://example.com'
if (isValidUrl(url)) {
  editor.commands.addLink({ href: url })
} else {
  console.error('Invalid URL')
}
  • Combine with text formatting extensions like defineBold() and defineItalic()
  • Use with defineCode() for inline code that shouldn’t be linkified
  • Works within lists created with defineList()