blokhaus

FontPicker

Font family picker component.

FontPicker renders a vertical list of available font families, each displayed in its own typeface for a visual preview. It is primarily used inside the FloatingToolbar but can also be used standalone within a Lexical editor context.

Import

import { FontPicker } from "@blokhaus/core";
import type { FontFamilyEntry } from "@blokhaus/core";

Props

PropTypeDefaultDescription
fontsFontFamilyEntry[]DEFAULT_FONT_FAMILIESArray of available font families.
activeFontstring | nullrequiredThe currently active font family CSS value, or null if using the default font.
onClose() => voidrequiredCallback invoked after a font is selected. Typically used to close the picker dropdown.

Basic usage

The FontPicker is typically used inside the FloatingToolbar and does not need to be added separately. However, you can use it in a custom toolbar:

"use client";

import { useState } from "react";
import { FontPicker } from "@blokhaus/core";

function CustomFontControl() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div style={{ position: "relative" }}>
      <button onClick={() => setIsOpen(!isOpen)}>Font</button>

      {isOpen && (
        <div style={{ position: "absolute", top: "100%", zIndex: 50 }}>
          <FontPicker activeFont={null} onClose={() => setIsOpen(false)} />
        </div>
      )}
    </div>
  );
}

The FontFamilyEntry interface

Each font option conforms to this interface:

interface FontFamilyEntry {
  /** Human-readable label shown in the picker */
  label: string;
  /** CSS font-family value to apply. Empty string = remove / default. */
  value: string;
  /** Font stack used to render the preview text in the picker dropdown */
  preview: string;
}

Default font families

The default set includes four options:

LabelCSS ValuePreview Font Stack
Default(removes font override)var(--blokhaus-font-sans)
Serifvar(--blokhaus-font-serif)var(--blokhaus-font-serif)
Monovar(--blokhaus-font-mono)var(--blokhaus-font-mono)
Handvar(--blokhaus-font-hand)var(--blokhaus-font-hand)

Custom font families

Override the defaults by passing a custom FontFamilyEntry[]:

import type { FontFamilyEntry } from "@blokhaus/core";

const customFonts: FontFamilyEntry[] = [
  { label: "Default", value: "", preview: "system-ui, sans-serif" },
  { label: "Georgia", value: "Georgia, serif", preview: "Georgia, serif" },
  {
    label: "Courier",
    value: '"Courier New", monospace',
    preview: '"Courier New", monospace',
  },
  {
    label: "Comic Sans",
    value: '"Comic Sans MS", cursive',
    preview: '"Comic Sans MS", cursive',
  },
];

<FontPicker fonts={customFonts} activeFont={null} onClose={() => {}} />;

To add web fonts (e.g., Google Fonts), ensure the font is loaded in your application first, then reference it in the value and preview fields:

const googleFonts: FontFamilyEntry[] = [
  { label: "Default", value: "", preview: "system-ui, sans-serif" },
  {
    label: "Inter",
    value: '"Inter", sans-serif',
    preview: '"Inter", sans-serif',
  },
  {
    label: "Playfair Display",
    value: '"Playfair Display", serif',
    preview: '"Playfair Display", serif',
  },
  {
    label: "JetBrains Mono",
    value: '"JetBrains Mono", monospace',
    preview: '"JetBrains Mono", monospace',
  },
];

Command dispatched

When a font is selected, the FontPicker dispatches:

  • SET_FONT_FAMILY_COMMAND with the CSS font-family value string

An empty string value ("") removes the font override, reverting to the editor's default font.

After dispatching the command, the picker calls editor.focus() to return focus to the editor and then invokes the onClose callback.

Visual indicators

  • The active font is highlighted with a subtle background color (--blokhaus-hover-bg).
  • A checkmark icon appears to the right of the active font label, colored with --blokhaus-accent.
  • Each font label is rendered in its own preview font stack, giving users an immediate visual preview of what the font looks like.

Notes

  • FontPicker is a client component ('use client').
  • It must be rendered within a LexicalComposer context (i.e., inside EditorRoot).
  • The component uses onMouseDown with e.preventDefault() on the container to prevent the editor from losing focus when interacting with the picker.
  • Font values use CSS variable references by default (e.g., var(--blokhaus-font-serif)) so that the actual font stacks can be customized via tokens.css without modifying the picker.
  • The minimum width of the picker dropdown is 160px.