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-popoverComponents
<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 '@/components/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 '@/components/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)