---
title: "Font Family Combobox"
description: "Add a searchable, grouped font-family picker with live previews to your Tiptap editor. More in the documentation!"
canonical_url: "https://tiptap.dev/docs/ui-components/components/font-family-combobox"
---

# Font Family Combobox

Add a searchable, grouped font-family picker with live previews to your Tiptap editor. More in the documentation!

A searchable font-family picker for Tiptap editors. The trigger shows the active font; opening it reveals a fuzzy search field and fonts grouped by category, each previewed in its own typeface. A “Default font” option unsets any inline font.

It is a richer, searchable counterpart to the `font-family-dropdown-menu`, and it pairs with the `FontFamily` mark from `@tiptap/extension-text-style`. The component is **catalog-agnostic**: pass your own `fonts`, `categories`, and `defaultFont`, or rely on the bundled web-safe defaults. All editor logic lives in the `useFontFamilyCombobox` hook.

> **Interactive demo:** [font family combobox](https://template.tiptap.dev/preview/tiptap-ui/font-family-combobox)

## Installation

Add the component via the Tiptap CLI:

```bash
npx @tiptap/cli@latest add font-family-combobox
```

## Components

### `<FontFamilyCombobox />`

A prebuilt searchable font picker built on Ariakit’s menu-with-combobox composite.

#### Usage

```tsx
export default function MyEditor() {
  return (
    <FontFamilyCombobox
      editor={editor}
      hideWhenUnavailable={true}
      onOpenChange={(isOpen) => console.log('Picker', isOpen ? 'opened' : 'closed')}
    />
  )
}
```

To use your own catalog, supply `fonts`, `categories`, and `defaultFont`. Each option’s `category` is matched against a category `id`, and an option’s optional `fallbackFor` lets imported system fonts (e.g. `"Arial"`) resolve to a bundled family.

```tsx
const fonts = [
  { value: 'inter', label: 'Inter', family: 'Inter', hint: 'UI sans', category: 'sans-serif', default: true },
  { value: 'georgia', label: 'Georgia', family: 'Georgia, serif', hint: 'Serif', category: 'serif' },
]
const categories = [
  { id: 'sans-serif', label: 'Sans-serif' },
  { id: 'serif', label: 'Serif' },
]

<FontFamilyCombobox editor={editor} fonts={fonts} categories={categories} defaultFont={fonts[0]} />
```

#### Props

The component also forwards any extra `Button` props (except `type`) to the trigger.

| Name                  | Type                   | Default                       | Description                                                         |
| --------------------- | ---------------------- | ----------------------------- | ------------------------------------------------------------------- |
| `editor`              | `Editor \| null`       | `undefined`                   | The Tiptap editor instance.                                         |
| `fonts`               | `FontFamilyOption[]`   | `defaultFontFamilyOptions`    | The fonts to offer in the picker.                                   |
| `defaultFont`         | `FontFamilyOption`     | `defaultFontFamilyOption`     | The font the document falls back to when no inline font is applied. |
| `categories`          | `FontFamilyCategory[]` | `defaultFontFamilyCategories` | Category groups to render, matched against each font’s `category`.  |
| `hideWhenUnavailable` | `boolean`              | `false`                       | Hides the picker when font family cannot be applied.                |
| `onOpenChange`        | `(boolean) => void`    | `undefined`                   | Callback for when the picker opens or closes.                       |

#### Types

```tsx
interface FontFamilyOption {
  label: string // display name in the picker
  value: string // stable identifier
  family: string // CSS font-family applied to the document; previewed in this typeface
  hint?: string // short descriptor under the label, e.g. "Calibri-like"
  category: string // group id (matched against a FontFamilyCategory)
  default?: boolean // marks the default option (no inline font)
  fallbackFor?: string[] // system fonts this family stands in for on import
}

interface FontFamilyCategory {
  id: string
  label: string
}
```

## Hooks

### `useFontFamilyCombobox()`

A headless hook exposing the selection state and actions needed to build a custom font picker.

#### Usage

```tsx
function MyFontPicker() {
  const { isVisible, triggerLabel, selectFont, selectDefault, filterFonts } = useFontFamilyCombobox(
    { editor, hideWhenUnavailable: true },
  )

  if (!isVisible) return null

  const results = filterFonts('geo') // ranked matches for a search query
  // …render your own UI using selectFont(font) / selectDefault()
}
```

#### Props

| Name                  | Type                 | Default                    | Description                                              |
| --------------------- | -------------------- | -------------------------- | -------------------------------------------------------- |
| `editor`              | `Editor \| null`     | `undefined`                | The Tiptap editor instance.                              |
| `fonts`               | `FontFamilyOption[]` | `defaultFontFamilyOptions` | The fonts to offer.                                      |
| `defaultFont`         | `FontFamilyOption`   | `defaultFontFamilyOption`  | The document’s fallback font when nothing inline is set. |
| `hideWhenUnavailable` | `boolean`            | `false`                    | Hides the picker when font family is unavailable.        |

#### Return Values

| Name               | Type                                    | Description                                                       |
| ------------------ | --------------------------------------- | ----------------------------------------------------------------- |
| `isVisible`        | `boolean`                               | Whether the picker should be rendered.                            |
| `canToggle`        | `boolean`                               | Whether font family can be applied in the current state.          |
| `isActive`         | `boolean`                               | Whether an explicit inline font is active (not the default).      |
| `fonts`            | `FontFamilyOption[]`                    | The configured fonts.                                             |
| `defaultFont`      | `FontFamilyOption`                      | The configured default font.                                      |
| `selectionState`   | `FontFamilySelectionState`              | The selection’s font state (`default` / `font` / `mixed`).        |
| `activeFontFamily` | `string`                                | The applied `font-family` for the selection (empty when default). |
| `activeFont`       | `FontFamilyOption \| undefined`         | The resolved option for the active family, if any.                |
| `triggerLabel`     | `string`                                | Label for the trigger (active font, `Mixed`, or `Default · …`).   |
| `selectFont`       | `(font: FontFamilyOption) => void`      | Apply a font family to the selection.                             |
| `selectDefault`    | `() => void`                            | Unset any inline font (use the document default).                 |
| `filterFonts`      | `(query: string) => FontFamilyOption[]` | Rank the configured fonts against a search query.                 |

## Utilities

### Default catalog

```tsx
import {
  defaultFontFamilyOptions,
  defaultFontFamilyCategories,
  defaultFontFamilyOption,
} from '@/components/tiptap-ui/font-family-combobox'
```

A web-safe set (Arial, Verdana, Tahoma, Times New Roman, Georgia, Courier New) grouped into Sans-serif / Serif / Monospace, so the component is usable out of the box without loading any webfont.

### Helpers

```tsx
import {
  filterFontsByQuery,
  findFontByFamily,
  resolveFontFamily,
  getActiveFontOption,
  getFontFamilySelectionState,
  getFontFamilyTriggerLabel,
} from '@/components/tiptap-ui/font-family-combobox'

filterFontsByQuery(fonts, 'geo') // ranked matches (blank query → unchanged order)
findFontByFamily(fonts, 'Georgia') // the option whose family matches, or undefined
resolveFontFamily(fonts, 'Arial') // map a system font through each option's fallbackFor
getActiveFontOption(fonts, 'Inter') // resolve an applied family to an option
getFontFamilySelectionState(editor) // { kind: 'default' | 'font' | 'mixed', fontFamily }
getFontFamilyTriggerLabel(state, defaultFont, fonts) // the trigger label string
```

## Requirements

### Dependencies

- `@tiptap/react` — Core Tiptap React integration
- `@tiptap/pm` — ProseMirror model/state used to read the selection’s font marks
- `match-sorter` — Fuzzy ranking for the search field
- `@tiptap/extension-text-style` — Provides the `FontFamily` mark this picker applies

### Referenced Components

- `use-tiptap-editor` (hook)
- `use-composed-ref` (hook)
- `font-family-dropdown-menu` (component — shared editor helpers)
- `button` (primitive)
- `combobox` (primitive)
- `input` (primitive)
- `menu` (primitive)
- `check-icon` (icon)
- `chevron-down-icon` (icon)
