---
title: "Link Popover"
description: "Select link options in a popover element with this Tiptap UI component. More in our documentation"
canonical_url: "https://tiptap.dev/docs/ui-components/components/link-popover"
---

# Link Popover

Select link options in a popover element with this Tiptap UI component. More in our documentation

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.

> **Interactive demo:** [link popover](https://template.tiptap.dev/preview/tiptap-ui/link-popover)

## Installation

Add the component via the Tiptap CLI:

```bash
npx @tiptap/cli@latest add link-popover
```

## Components

### `<LinkPopover />`

A prebuilt React component that provides a complete link editing interface in a popover.

#### Usage

```tsx
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

```tsx
<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

```tsx
<LinkContent editor={editor} />
```

## Hooks

### `useLinkPopover()`

A custom hook to build your own link interface with full control over rendering and behavior.

#### Usage

```tsx
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

```tsx
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.

```tsx
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.

```tsx
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)
