unifont
  • All fonts Browse or filter the whole catalogue
  • Compare providers One family, every CDN, side by side
  • Documentation Install, providers, custom providers
  • HTTP API The endpoints this site is built on

Getting started

Install unifont, resolve a family, and turn the answer into CSS.

unifont is ESM-only, and has no peer dependencies.

bash

npm i unifont

Create an instance

createUnifont() takes an array of providers. Order matters. They're tried in turn, and the first one that knows the family wins.

ts

import { createUnifont, providers } from 'unifont'

const unifont = await createUnifont([
  providers.google(),
  providers.fontshare(),
  providers.fontsource(),
])

Ask what a family has

getFontProperties() tells you what a family publishes before you resolve anything. It returns undefined if no provider recognises the name.

ts

const properties = await unifont.getFontProperties('Newsreader')

properties?.weights   // ['200 800'], a variable range
properties?.styles    // ['normal', 'italic']
properties?.subsets   // ['cyrillic', 'greek', 'latin', 'latin-ext', 'vietnamese']
properties?.provider  // 'google'

A missing field means the provider doesn't publish that information.

Variable families report a range as one '<min> <max>' string, rather than listing every weight.

Resolve the faces

ts

const { fonts, fallbacks, provider } = await unifont.resolveFont('Newsreader', {
  weights: ['400', '600'],
  styles: ['normal'],
  subsets: ['latin'],
})

fonts is an array of @font-face descriptors: src, weight, style, unicodeRange, and a meta.subset label if the provider gave one.

fallbacks holds the generic family the provider suggests (serif, sans-serif). (That's what you need if you're generating metric-adjusted fallbacks.)

Turn it into CSS

unifont stops at the data. Writing it out takes a few lines, and doing it yourself means the CSS comes out the way you want it:

ts

const css = fonts.map((face) => {
  const src = face.src
    .map(source => 'name' in source
      ? `local("${source.name}")`
      : `url("${source.url}")${source.format ? ` format("${source.format}")` : ''}`)
    .join(', ')

  return [
    '@font-face {',
    `  font-family: "Newsreader";`,
    `  src: ${src};`,
    face.weight ? `  font-weight: ${Array.isArray(face.weight) ? face.weight.join(' ') : face.weight};` : '',
    `  font-style: ${face.style ?? 'normal'};`,
    `  font-display: ${face.display ?? 'swap'};`,
    face.unicodeRange ? `  unicode-range: ${face.unicodeRange.join(', ')};` : '',
    '}',
  ].filter(Boolean).join('\n')
}).join('\n\n')

Or don't bother. Every family page on this site serves the same CSS at /api/v1/fonts/<family>/css, and the API reference lists the parameters.

Errors

If a provider fails, unifont logs it and carries on, so one dead API doesn't break your build. Pass throwOnError if you'd rather it stopped:

ts

const unifont = await createUnifont([providers.google()], { throwOnError: true })