Link
The Link mark applies hyperlink formatting to text. It renders as <a> with href, target, rel, title, and class attributes. Links are semantic data, not formatting - they survive unsetAllMarks() (clear formatting). Included in StarterKit by default.
Live Playground
Section titled “Live Playground”Select text and apply a link with buttons, keyboard shortcut (Cmd+K / Ctrl+K), or type/paste a URL to see autolink in action.
With the default theme enabled. Click the link toolbar button or use Cmd+K / Ctrl+K to open the URL popover.
Vanilla JS preview - Angular components produce the same output
Vanilla JS preview - React components produce the same output
Plain editor without the theme. The buttons call setLink(), unsetLink(), and toggleLink() with a sample URL.
Link is included in StarterKit, so it works out of the box. When using StarterKit, Mod-K opens the LinkPopover with a URL input. URLs are auto-detected when typing (autolink) and when pasting.
To use Link standalone:
With StarterKit, the link toolbar button and Mod-K shortcut open the built-in URL popover automatically:
import { Editor, StarterKit } from '@domternal/core';import '@domternal/theme';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [StarterKit], content: '<p>Select text and press <code>Cmd+K</code> to add a link. Try typing a URL like example.com followed by a space.</p>',});Without StarterKit (standalone Link, no popover), handle the linkEdit event yourself:
import { Editor, Document, Paragraph, Text, Link } from '@domternal/core';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, Link], content: '<p>Visit <a href="https://example.com">Example</a> for more.</p>',});
// Mod-K emits linkEdit - show your own UIeditor.on('linkEdit', () => { const url = prompt('URL:'); if (url) editor.chain().focus().setLink({ href: url }).run();});import { Component, signal } from '@angular/core';import { DomternalEditorComponent, DomternalToolbarComponent } from '@domternal/angular';import { Editor, StarterKit } from '@domternal/core';
@Component({ selector: 'app-editor', imports: [DomternalEditorComponent, DomternalToolbarComponent], templateUrl: './editor.html',})export class EditorComponent { editor = signal<Editor | null>(null); extensions = [StarterKit]; content = '<p>Select text and press Cmd+K to add a link.</p>';}@if (editor(); as ed) { <domternal-toolbar [editor]="ed" />}<domternal-editor [extensions]="extensions" [content]="content" (editorCreated)="editor.set($event)"/>With StarterKit, the link toolbar button opens the built-in URL popover. Autolink and paste detection work automatically.
import { Domternal } from '@domternal/react';import { StarterKit } from '@domternal/core';
export default function Editor() { return ( <Domternal extensions={[StarterKit]} content="<p>Select text and press Cmd+K to add a link.</p>" > <Domternal.Toolbar /> <Domternal.Content /> </Domternal> );}With StarterKit, the link toolbar button opens the built-in URL popover. Autolink and paste detection work automatically.
import { Editor, Document, Paragraph, Text, Link } from '@domternal/core';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, Link], content: '<p>Visit <a href="https://example.com">Example</a> for more.</p>',});
// Set a link on the current selectioneditor.chain().focus().setLink({ href: 'https://example.com' }).run();
// Remove a linkeditor.commands.unsetLink();
// Toggle: remove if active, set if noteditor.chain().focus().toggleLink({ href: 'https://example.com' }).run();Without LinkPopover, Mod-K emits a linkEdit event but no UI appears. Listen for the event to build your own link input.
To disable Link in StarterKit:
StarterKit.configure({ link: false })Schema
Section titled “Schema”| Property | Value |
|---|---|
| ProseMirror name | link |
| Type | Mark |
| HTML tag | <a> |
| Priority | 1000 |
| isFormatting | false |
The inclusive property is dynamic: when autolink is enabled, the mark is inclusive so typing at the end of a link extends it. When autolink is off, links are not inclusive.
Attributes
Section titled “Attributes”| Attribute | Type | Default | Description |
|---|---|---|---|
href | string | null | URL target |
target | string | null | null | Link target (e.g. _blank) |
rel | string | null | null | Relationship (e.g. noopener noreferrer) |
title | string | null | null | Tooltip text |
class | string | null | null | CSS classes |
Parsing rules
Section titled “Parsing rules”| Source | Condition |
|---|---|
<a> | Must have href attribute with a valid URL (validated against protocols) |
The parser extracts all 5 attributes (href, target, rel, title, class) from the <a> element. Invalid URLs are rejected.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
HTMLAttributes | Record<string, unknown> | {} | Custom HTML attributes added to the rendered <a> element |
protocols | string[] | ['http:', 'https:', 'mailto:', 'tel:'] | Allowed URL protocols |
openOnClick | boolean | 'whenNotEditable' | true | When to open links on click |
addRelNoopener | boolean | true | Auto-add rel="noopener noreferrer" to target="_blank" links |
autolink | boolean | true | Auto-convert typed URLs to links |
linkOnPaste | boolean | true | Convert pasted URLs to links |
defaultProtocol | string | 'https' | Default protocol for bare URLs |
shouldAutoLink | (url: string) => boolean | undefined | Custom validation for autolink |
enableClickSelection | boolean | false | Select full link text on click |
import { Link } from '@domternal/core';
const CustomLink = Link.configure({ protocols: ['http:', 'https:'], openOnClick: 'whenNotEditable', autolink: true, linkOnPaste: true, defaultProtocol: 'https',});Commands
Section titled “Commands”setLink
Section titled “setLink”Applies a link to the current selection. Validates the href against allowed protocols.
editor.commands.setLink({ href: 'https://example.com' });
// With targeteditor.commands.setLink({ href: 'https://example.com', target: '_blank',});Returns false if the URL is invalid.
unsetLink
Section titled “unsetLink”Removes the link from the current selection. If the cursor is empty, removes the link from the entire link range around the cursor.
editor.commands.unsetLink();toggleLink
Section titled “toggleLink”Toggles a link on the current selection. If the cursor is in a link, removes it. If not, applies the link.
editor.commands.toggleLink({ href: 'https://example.com' });
// With chainingeditor.chain().focus().toggleLink({ href: 'https://example.com' }).run();Keyboard shortcuts
Section titled “Keyboard shortcuts”| Key | Action |
|---|---|
Mod-K | Open link popover (linkEdit event) |
Mod is Cmd on macOS and Ctrl on Windows/Linux.
Input rules
Section titled “Input rules”Link has no input rules. URLs are detected through the autolink plugin (type a URL followed by a space or punctuation) and the paste plugin (paste a URL to wrap the selection or insert as a link).
Plugins
Section titled “Plugins”Link creates up to 5 ProseMirror plugins:
| Plugin | Condition | Description |
|---|---|---|
| linkClick | Always | Opens links on click (configurable via openOnClick) |
| linkPaste | linkOnPaste: true | Converts pasted URLs to links |
| linkExit | Always | Pressing ArrowRight at link boundary exits the mark |
| linkKeepOnSplit | Always | Pressing Enter at link end does not carry the link to the new line |
| autolink | autolink: true | Auto-converts typed URLs to links on space/punctuation |
Toolbar items
Section titled “Toolbar items”Link registers a button in the toolbar with the name link in group format at priority 120.
| Item | Type | Command | Icon | Shortcut | Active when |
|---|---|---|---|---|---|
| Link | button | unsetLink | link | Mod-K | Link mark is active on selection |
The toolbar button also has emitEvent: 'linkEdit', which triggers the LinkPopover to open when clicked.
Exports
Section titled “Exports”import { Link } from '@domternal/core';import type { LinkOptions, LinkAttributes } from '@domternal/core';| Export | Type | Description |
|---|---|---|
Link | Mark extension | The link mark extension |
LinkOptions | TypeScript type | Options for Link.configure() |
LinkAttributes | TypeScript type | Attributes for setLink() / toggleLink() |
Link also exports its plugin helpers:
import { linkClickPlugin, linkClickPluginKey, linkPastePlugin, linkPastePluginKey, autolinkPlugin, autolinkPluginKey, linkExitPlugin, linkExitPluginKey,} from '@domternal/core';How it works
Section titled “How it works”Link uses two ProseMirror plugins internally:
-
Autolink plugin - Watches for text that matches URL patterns as you type. When a valid URL is detected (ending with a space, Enter, or punctuation), it is automatically wrapped in a link mark. Autolink respects the
autolinkoption and only triggers outside code blocks. -
Paste handler plugin - When you paste a URL over selected text, the text is converted into a link with that URL instead of replacing the text. When you paste a URL without selection, the URL is inserted as a clickable link. Both behaviors respect the
protocolsallowlist.
The LinkPopover extension (included in StarterKit) provides the floating UI for editing link URLs. When the cursor is inside a link, the popover shows the URL with edit and remove buttons. Pressing Mod-K opens the popover to create a new link or edit an existing one.
Source
Section titled “Source”@domternal/core - Link.ts