@docs.plus/extension-hyperlink
v2.1.0
Published
Tiptap extension for hyperlinks with built-in popovers, auto-linking, and 50+ URL scheme support
Downloads
1,194
Maintainers
Readme
@docs.plus/extension-hyperlink
Tiptap hyperlink mark with optional prebuilt popovers for creating, previewing, and editing links.
It replaces the @tiptap/extension-link mark that StarterKit v3 bundles.
Why use it
- Autolink covers more than web URLs. As you type, bare domains get
https://, emails becomemailto:, and E.164 phones becometel:. App schemes from a catalog of 47, such aswhatsapp://, autolink too. - The link UI is included. Prebuilt create, preview, and edit popovers ship with the package.
Mod-kopens the create-link form with no configuration.@tiptap/extension-linkbinds no keyboard shortcut. - Every boundary checks the scheme.
isSafeHrefblocksjavascript:,data:,vbscript:,file:, andblob:on parse, import, write, serialize, and navigation. An embedded tab or newline cannot hide a scheme from the gate. - Your
@tiptap/extension-linkconfiguration ports over.isAllowedUritakes the same(uri, ctx)shape as@tiptap/extension-link, so an existing policy works unchanged.setLink,unsetLink, andtoggleLinkstay as aliases.[label](url)round-trips with@tiptap/markdown.
Install
npm install @docs.plus/extension-hyperlinkOr use pnpm add @docs.plus/extension-hyperlink, yarn add @docs.plus/extension-hyperlink, or bun add @docs.plus/extension-hyperlink.
Requires @tiptap/core ^3.31.3 and @tiptap/pm ^3.31.3 (Tiptap 3.x).
This package imports no React, Vue, or Next.js code. Use it from a plain page, Vite, React, Vue, Svelte, Next.js, Nuxt, or SvelteKit. Create the editor in the browser.
React Native has no DOM. Load the editor in a web view.
Installs two runtime dependencies, @floating-ui/dom and linkifyjs. The popover engine and the tooltip ship inside dist, so they add no third package.
Upgrading from 1.x? Option names, command names, and CSS class names all changed — see Migrating from 1.x.
Quickstart
Run this Quickstart in your browser first, with nothing to install. The app lives in examples/vanilla.
The host page needs one mount point: <div id="editor"></div>. The snippet also imports @tiptap/starter-kit. Add it with npm install @tiptap/starter-kit when your app has none yet.
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import {
Hyperlink,
createHyperlinkPopover,
editHyperlinkPopover,
previewHyperlinkPopover
} from '@docs.plus/extension-hyperlink'
import '@docs.plus/extension-hyperlink/styles.css'
const editor = new Editor({
element: document.querySelector('#editor'),
content: '<p>Try <a href="https://example.com">this link</a>.</p>',
extensions: [
// Disable StarterKit's bundled link mark — see Caveats.
StarterKit.configure({ link: false }),
Hyperlink.configure({
popovers: {
previewHyperlink: previewHyperlinkPopover,
editHyperlink: editHyperlinkPopover,
createHyperlink: createHyperlinkPopover
}
})
]
})The styles.css import is optional. The snippet also styles no document link — see Popovers and Style document links.
You should see a paragraph with one link. Click the link to open the preview popover, with copy, edit, and remove actions. Type example.org and a space, and the text becomes a link. Select a word and press Mod-k (Cmd on macOS, Ctrl elsewhere) to open the create-link form.
Caveats
StarterKit.configure({ link: false })is required, not stylistic. StarterKit v3 bundles@tiptap/extension-linkby default. The two marks collide on thesetLink/unsetLink/toggleLinkcommand names and on thea[href]parse rule. Yourextensionsarray order then decides each contest, with no warning. If the upstream mark wins, this package'sisSafeHrefgate never runs on parsed or pasted HTML, and its popovers never attach.- A click on a link needs
popovers.previewHyperlink. The three openers fall back to the prebuilt factory when a slot isnull. The click handler does not: leavepreviewHyperlinkatnulland a click on a link opens no popover. linkOnPaste: falsedoes not stop every paste from linking. The linkify paste rule stays registered, so a pasted bare URL still becomes a link. To stop that path too, setshouldAutoLink: () => false, which every autolink path consults.HTMLAttributes.targetreaches the DOM.HTMLAttributes: { target: '_blank' }does emit<a target="_blank">. A browser-driventarget="_blank"navigation skips the click gate. Leavetargetatnulland let the click handler open the link.
The full list, with the reason for each, is in Caveats.
Common tasks
Set a link from your own button
Every command lands on editor.commands and on editor.chain(). On editor.commands, each returns boolean, and false marks a no-op.
editor.chain().focus().setHyperlink({ href: 'https://example.com' }).run()
editor.getAttributes('hyperlink').href // read the current hrefsetHyperlink returns false when href is empty, when the composed gate rejects it, or when the schema cannot apply the mark there. See Commands for the full list.
Open the create-link form
Mod-k calls this command. Call it from your own toolbar button too:
editor.commands.openCreateHyperlinkPopover()openCreateHyperlinkPopover opens UI, so editor.can().openCreateHyperlinkPopover() reports availability without mounting anything. With nothing selected, the prebuilt form inserts the typed URL as its own link text.
Style document links
The styles.css stylesheet skins the popovers only. The mark renders a plain <a> with no class, so style your document links yourself:
Hyperlink.configure({ HTMLAttributes: { class: 'my-link' } })a.my-link {
color: #2563eb;
text-decoration: underline;
}For theme tokens, dark mode, and class names, see Styling.
Gallery
Prebuilt create, preview, and edit popovers (styles.css + popovers config). Each screenshot has a light and a dark version, and follows your system preference.
Documentation
| Guide | What it covers |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| API reference | Options, commands, keyboard shortcuts, the full caveats list, server bundles, and TypeScript exports |
| Popovers | The three popover slots, factory option shapes, openers, and custom popovers |
| Styling | The stylesheet, --hl-* theme tokens, dark mode, and class names |
| URL handling | Href normalization, scheme classification, and Markdown round-trip |
| Security | The dangerous-scheme gate at each boundary, and the exported safety helpers |
| Advanced | The floating-popover primitive, the UI controller, and the tooltip primitive |
| Migrating from 1.x | Renamed options, commands, CSS classes, and popover APIs |
| Changelog | Every release and its breaking changes |
| For AI coding agents | This README as plain Markdown for your agent. The docs are also on Context7 |
Part of docs.plus
This extension is built for and maintained by docs.plus. docs.plus is a free, real-time collaboration tool that lets communities organize knowledge hierarchically, with a chat thread on every heading. docs.plus runs these packages from source in production, so every release is exercised there before it reaches npm.
- Website: docs.plus
- Project README: docs-plus/docs.plus
- Sibling extensions and recommended pairings: extensions/README.md
Contributing
Bug reports and PRs welcome. Setup, test commands, and the playground harness live in CONTRIBUTING.md.
License
MIT — see LICENSE.
