Link Popover
A fully accessible link popover for Tiptap editors. Easily add, edit, and remove links with a user-friendly popover interface that supports keyboard shortcuts and flexible customization options.
Installation
Add the component via the Tiptap CLI:
npx @tiptap/cli@latest add link-popover
Components
<LinkPopover />
A prebuilt React component that provides a complete link editing interface in a popover.
Usage
import { EditorContent, EditorContext, useEditor } from '@tiptap/react'
import { StarterKit } from '@tiptap/starter-kit'
import { Link } from '@/components/tiptap-extension/link-extension'
import { LinkPopover } from '@/components/tiptap-ui/link-popover'
import '@/components/tiptap-node/paragraph-node/paragraph-node.scss'
export default function MyEditor() {
const editor = useEditor({
immediatelyRender: false,
extensions: [StarterKit, Link.configure({ openOnClick: false })],
content: `
<p>Click the button to open the link popover.</p>
<p><a href="https://www.tiptap.dev">Tiptap</a></p>
`,
})
return (
<EditorContext.Provider value={{ editor }}>
<LinkPopover
editor={editor}
hideWhenUnavailable={true}
autoOpenOnLinkActive={true}
onSetLink={() => console.log('Link set!')}
onOpenChange={(isOpen) => console.log('Popover opened:', isOpen)}
/>
<EditorContent editor={editor} role="presentation" />
</EditorContext.Provider>
)
}
Props
Name | Type | Default | Description |
---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
hideWhenUnavailable | boolean | false | Hides the button when link functionality is not available |
onSetLink | () => void | undefined | Callback fired after a link is successfully set |
onOpenChange | (isOpen: boolean) => void | undefined | Callback fired when the popover opens or closes |
autoOpenOnLinkActive | boolean | true | Whether to automatically open when a link is active |
<LinkButton />
A standalone link button component for triggering link functionality.
Usage
<LinkButton onClick={handleClick} aria-label="Add link">
Custom Link Content
</LinkButton>
<LinkContent />
A standalone component that renders the link editing interface without the popover wrapper.
Usage
<LinkContent editor={editor} />
Hooks
useLinkPopover()
A custom hook to build your own link interface with full control over rendering and behavior.
Usage
function MyLinkButton() {
const { isVisible, canSet, isActive, url, setUrl, setLink, removeLink, label, Icon } =
useLinkPopover({
editor: myEditor,
hideWhenUnavailable: true,
onSetLink: () => console.log('Link set!'),
})
if (!isVisible) return null
return (
<div>
<button onClick={setLink} disabled={!canSet} aria-label={label} aria-pressed={isActive}>
<Icon />
{label}
</button>
<input
type="url"
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder="Enter URL..."
/>
<button onClick={removeLink}>Remove</button>
</div>
)
}
Props
Name | Type | Default | Description |
---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
hideWhenUnavailable | boolean | false | Hides functionality if link cannot be applied |
onSetLink | () => void | undefined | Callback fired after setting a link |
Return Values
Name | Type | Description |
---|---|---|
isVisible | boolean | Whether the link functionality should be rendered |
canSet | boolean | If a link can be set in the current context |
isActive | boolean | If a link is currently active/selected |
url | string | Current URL value for the link |
setUrl | React.Dispatch<React.SetStateAction<string | null>> | Function to update the URL state |
setLink | () => void | Function to apply the link in the editor |
removeLink | () => void | Function to remove the link from the editor |
label | string | Accessible label text for the button |
Icon | React.FC | Icon component for the link button |
useLinkHandler()
A focused hook for handling link operations without UI state management.
Usage
function MyCustomLinkInterface() {
const { url, setUrl, setLink, removeLink } = useLinkHandler({
editor: myEditor,
onSetLink: () => console.log('Link applied!'),
})
return (
<div>
<input
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && setLink()}
/>
<button onClick={setLink}>Apply</button>
<button onClick={removeLink}>Remove</button>
</div>
)
}
Props
Name | Type | Default | Description |
---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
onSetLink | () => void | undefined | Callback fired after setting a link |
Return Values
Name | Type | Description |
---|---|---|
url | string | Current URL value for the link |
setUrl | React.Dispatch<React.SetStateAction<string | null>> | Function to update the URL state |
setLink | () => void | Function to apply the link in the editor |
removeLink | () => void | Function to remove the link from the editor |
Utilities
canSetLink(editor)
Checks if a link can be set in the current editor state.
import { canSetLink } from '@/registry/tiptap-ui/link-popover'
const canSet = canSetLink(editor)
if (canSet) {
console.log('Link can be applied to current selection')
}
isLinkActive(editor)
Checks if a link is currently active in the editor.
import { isLinkActive } from '@/registry/tiptap-ui/link-popover'
const isActive = isLinkActive(editor)
if (isActive) {
console.log('A link is currently selected')
}
Keyboard Shortcuts
The link popover supports the following keyboard interactions:
- Enter: Apply the current URL as a link (when focused in the URL input)
- Escape: Close the popover (standard popover behavior)
Requirements
Dependencies
@tiptap/react
- Core Tiptap React integration@tiptap/extension-link
- Link extension for link functionality
Referenced Components
use-tiptap-editor
(hook)use-mobile
(hook)use-link-popover
(hook)button
(primitive)popover
(primitive)card
(primitive)input
(primitive)separator
(primitive)tiptap-utils
(lib)corner-down-left-icon
(icon)external-link-icon
(icon)link-icon
(icon)trash-icon
(icon)