npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uixo/font-picker

v0.1.3

Published

Searchable font picker for Google Fonts + Fontshare, with pre-rendered sprite previews and Tailwind v4 theme hooks.

Readme

@uixo/font-picker

A searchable React font picker for Google Fonts + Fontshare — ~1,870 families in a single virtualized list, each rendered in its own typeface from a pre-generated sprite sheet. Ships with a Tailwind v4 theme so every family is usable as a font-<slug> utility.

  • Panel or dropdown variant (the dropdown auto-flips above the input when space is tight).
  • Works with Tailwind v4 — registers font-oswald, font-playfair-display, font-big-shoulders-stencil-text, etc. as first-class utilities that compose with font-bold, italic, and so on.
  • Unified callback — one onSelect(selection) payload gives you the family name, the Tailwind class, the CSS import URL (Google) or self-contained @font-face rules (Fontshare), so you can actually use the font in your code.
  • Data-driven — sprite previews, sprite coordinates, and font metadata live on a CDN (Cloudflare R2 by default). The component lazy-fetches them at runtime; there's no giant client bundle.

Install

npm install @uixo/font-picker

Peer deps: react >= 18, react-dom >= 18. Assumes your project uses Tailwind CSS v4.

Quick start

import { FontPicker, type FontSelection } from "@uixo/font-picker";

export function FontField() {
  return (
    <div style={{ height: 480, width: 520 }}>
      <FontPicker
        onSelect={(sel: FontSelection) => {
          console.log(sel.family, sel.tailwindClass);
          // do whatever — paste sel.linkTag into <head>, use sel.tailwindClass, etc.
        }}
      />
    </div>
  );
}

That's the minimum to see something on screen. The sections below cover the Tailwind setup, the callback payload, and every prop.

Setup (Tailwind v4)

Two lines in your main Tailwind CSS file.

@import "tailwindcss";

/* 1. Register every family as a Tailwind font-<slug> utility */
@import "@uixo/font-picker/theme.css";

/* 2. Tell Tailwind to scan the compiled component for utility classes */
@source "../node_modules/@uixo/font-picker/dist/**/*.js";

(Adjust the @source path relative to your CSS file. From a typical src/index.css in a Vite app, ../node_modules/... is correct.)

Optional: pre-register all Fontshare fonts at build time

@import "@uixo/font-picker/fontshare.fonts.css";

This adds @font-face rules for every Fontshare family. Useful if you want Fontshare fonts instantly available; otherwise the component injects @font-face lazily when a Fontshare font is selected.

Google Fonts are always loaded lazily via their CSS endpoint on selection.

The onSelect callback

Every row click fires onSelect(selection) with a complete payload describing the chosen font and exactly what you need to actually render it:

interface FontSelection {
  family: string;                    // "Oswald"
  source: "google" | "fshare";
  category: "sans-serif" | "serif" | "display" | "handwriting" | "monospace";
  weights: number[];                 // [200, 300, 400, 500, 600, 700]
  hasItalic: boolean;

  // Tailwind v4 utility that resolves to this family
  tailwindClass: string;             // "font-oswald"
  cssVarName: string;                // "--font-oswald"

  // Google Fonts only — null for Fontshare
  importUrl: string | null;          // "https://fonts.googleapis.com/css2?family=…"
  linkTag: string | null;            // "<link href=\"…\" rel=\"stylesheet\" />"
  cssImport: string | null;          // "@import url(\"…\");"

  // Fontshare only — null for Google
  // Self-contained @font-face rules covering every weight + italic. Inline
  // these directly in a <style> tag or paste into a CSS file.
  fontFaceCss: string | null;
}

Typical usage — wire the font into the current page:

<FontPicker
  onSelect={(sel) => {
    if (sel.importUrl) {
      // Google — inject a <link> once
      const id = `font-${sel.family}`;
      if (!document.getElementById(id)) {
        const link = document.createElement("link");
        link.id = id;
        link.rel = "stylesheet";
        link.href = sel.importUrl;
        document.head.appendChild(link);
      }
    } else if (sel.fontFaceCss) {
      // Fontshare — inject @font-face rules once
      const id = `font-${sel.family}`;
      if (!document.getElementById(id)) {
        const style = document.createElement("style");
        style.id = id;
        style.textContent = sel.fontFaceCss;
        document.head.appendChild(style);
      }
    }
    // Now apply: className={sel.tailwindClass}, or style={{ fontFamily: sel.family }}
  }}
/>

Props

| Prop | Type | Default | Description | | ---------------- | -------------------------------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | | onSelect | (sel: FontSelection) => void | — | Required. Fires when a font row is clicked. | | variant | "panel" \| "dropdown" | "panel" | See Variants below. | | width | number \| string | 360 (dropdown) / "100%" (panel) | Component width. | | maxHeight | number | 480 | In panel, caps the root height. In dropdown, caps the popover height (auto-clamps further to the viewport). | | placeholder | string | "Search fonts…" | Input placeholder. | | name | string | "fontname" | name attribute on the search input. Set this when you're embedding the picker in a form. | | value | string | — | Currently selected font id (${source}-${familySlug}-${weightSlug}). Used to highlight the active row. | | className | string | — | Extra class on the root element. | | fonts | FontEntry[] | fetched from assetsBaseUrl | Pre-fetched fonts manifest. Pass this if you already have fonts.json in memory (e.g. loaded once globally). | | sprites | SpritesManifest | fetched from assetsBaseUrl | Pre-fetched sprite coordinates manifest. | | assetsBaseUrl | string | Maintainer's R2 bucket | Where fonts.json, sprites.manifest.json, and sprites/* are hosted. Trailing slash is optional. |

All types (FontEntry, SpritesManifest, FontsManifest, FontSelection, FontSource, FontCategory) are exported.

Variants

panel (default)

All controls render in-flow inside a bordered card. Size it with width / maxHeight, or let it fill a parent container.

<div className="h-[480px] w-[520px]">
  <FontPicker onSelect={...} />
</div>

dropdown

Only the search input is visible. Focusing the input opens a popover containing the source toggle, category chips, and the virtualized list. Closes on outside click or Escape. Powered by Radix Popover, so it auto-flips above the input if there isn't room below.

<FontPicker variant="dropdown" width={420} onSelect={...} />

Theming

Colors are CSS custom properties with baked-in fallbacks, so nothing is required — but you can override any of them:

:root {
  --fp-bg: white;
  --fp-fg: #111;
  --fp-border: #e5e7eb;
  --fp-muted: #6b7280;         /* muted text */
  --fp-muted-bg: #f4f4f5;      /* chip background, selected row background */
  --fp-muted-hover: #e4e4e7;
  --fp-accent: #111;           /* active source toggle + category chip */
  --fp-accent-fg: #fff;
}

Data & hosting

The component needs four things reachable at ${assetsBaseUrl}:

  • fonts.json — metadata for every family
  • sprites.manifest.json — sprite coordinates keyed by previewId
  • sprites/spritesheet-*.png — the sprite sheets themselves
  • fontshare.fonts.css (optional) — if you chose to @import it in your CSS

By default these are fetched from a maintained Cloudflare R2 bucket. To self-host or pin a specific version, generate your own using the data pipeline in the source repo and set assetsBaseUrl accordingly:

<FontPicker assetsBaseUrl="https://your-cdn.example.com/fonts/" onSelect={...} />

License

MIT