Caching
Provider metadata barely changes. Cache it, and cold starts stop hurting.
By default unifont caches in memory, which lasts as long as the process does. For a build tool or a server that's usually the wrong choice, because every restart pays the full round trip for the same getFontProperties('Inter') call.
The storage option takes anything with getItem and setItem, so anything unstorage can drive will work:
import { createUnifont, providers } from 'unifont'
import { createStorage } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs-lite'
const storage = createStorage({
driver: fsDriver({ base: 'node_modules/.cache/unifont' }),
})
const unifont = await createUnifont([providers.google()], { storage })Entries expire after a week, and they're keyed by unifont version, so upgrading the package throws them away rather than handing you data in an old shape.
In Nitro or Nuxt
Nitro already has storage, so give unifont a thin adapter over it:
// server/utils/unifont.ts
import { createUnifont, providers } from 'unifont'
let instance: ReturnType<typeof createUnifont> | undefined
export function useUnifont() {
const cache = useStorage('unifont')
instance ??= createUnifont([providers.google()], {
storage: {
getItem: key => cache.getItem(key),
setItem: (key, value) => cache.setItem(key, value),
},
})
return instance
}That's what this site does. Point the unifont bucket at the filesystem in development and at KV in production, and family pages come back warm.
What is worth caching
Providers cache two things behind the same interface. There's the family index built at startup: listFonts data, large, and it hardly ever changes. And there are per-family results, keyed by the options you passed: small, but a lot of them. Both go through the storage you provide, so one driver covers it.
Keep the instance around too, as the example above does. createUnifont() starts up every provider, and for some that means fetching the whole family index. Creating one per request throws that work away.