Link Popover

Available for free

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

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance
hideWhenUnavailablebooleanfalseHides the button when link functionality is not available
onSetLink() => voidundefinedCallback fired after a link is successfully set
onOpenChange(isOpen: boolean) => voidundefinedCallback fired when the popover opens or closes
autoOpenOnLinkActivebooleantrueWhether 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

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance
hideWhenUnavailablebooleanfalseHides functionality if link cannot be applied
onSetLink() => voidundefinedCallback fired after setting a link

Return Values

NameTypeDescription
isVisiblebooleanWhether the link functionality should be rendered
canSetbooleanIf a link can be set in the current context
isActivebooleanIf a link is currently active/selected
urlstringCurrent URL value for the link
setUrlReact.Dispatch<React.SetStateAction<string | null>>Function to update the URL state
setLink() => voidFunction to apply the link in the editor
removeLink() => voidFunction to remove the link from the editor
labelstringAccessible label text for the button
IconReact.FCIcon 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

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance
onSetLink() => voidundefinedCallback fired after setting a link

Return Values

NameTypeDescription
urlstringCurrent URL value for the link
setUrlReact.Dispatch<React.SetStateAction<string | null>>Function to update the URL state
setLink() => voidFunction to apply the link in the editor
removeLink() => voidFunction 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)