---
title: "Upgrade Guide"
description: "Breaking changes and migration steps when upgrading to the latest version of @nuxt/fonts."
canonical_url: "https://nuxt.com/modules/fonts/upgrade"
---
# Upgrade Guide

> Breaking changes and migration steps when upgrading to the latest version of @nuxt/fonts.

## Upgrading to v1

v1 follows v0.14, as v0.15 was never released. If you are on v0.14, everything in this section applies to you.

### Breaking Changes

#### The default `400 700` weight range is applied

[`weights`](https://nuxt.com/modules/fonts/configuration#weights) has always been documented as defaulting to `['400 700']`, but the default was set on the wrong option and never reached the resolver, so families without an explicit `weights` were resolved at weight `400` alone. The documented default now takes effect.

A family you did not set `weights` on will resolve a bold face as well as a regular one, so you will see additional `@font-face` rules and an additional file downloaded per family. Text that silently fell back to a synthesised bold will now render in the real one.

Set `weights` explicitly to go back to a single weight:

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      weights: [400],
    },
  },
})
```

#### Fonts are served from `/_nuxt/fonts` in Vite builds

Production builds with the Vite builder now emit font files as Vite build assets under `app.buildAssetsDir`, so they are served from `/_nuxt/fonts/<hash>.woff2` rather than `/_fonts/<hash>.woff2`. Vite owns their URLs, which means `app.cdnURL`, a relative `app.baseURL` and `experimental.renderBuiltUrl` now apply to fonts as they do to every other built asset. `app.cdnURL` in particular was previously never applied.

The location of a font file is an implementation detail rather than a stable URL contract, so this may change again. If you have hard-coded `/_fonts/` anywhere (a CDN rule, a CSP directive, a cache header, a test), point it at your build assets directory instead. Fonts are still served from `/_fonts` in development and with the webpack and rspack builders; see [where fonts are served from](https://nuxt.com/modules/fonts/advanced#where-fonts-are-served-from).

#### Fonts served from a CDN need CORS headers

If you set `app.cdnURL`, your fonts are now fetched from that CDN rather than from your own origin. Browsers always fetch fonts in CORS mode, so the CDN must respond with an `Access-Control-Allow-Origin` header that covers your site. Without it, fonts that previously loaded fail silently and the browser falls back to the next family in the stack. Preload links for fonts carry the `crossorigin` attribute so the preloaded response is reused rather than fetched twice.

#### `assets.prefix` is relative to `buildAssetsDir` in Vite builds

[`assets.prefix`](https://nuxt.com/modules/fonts/configuration#assets) now names a directory inside `app.buildAssetsDir` in Vite production builds, and defaults to `fonts`. Setting `prefix: '/my-fonts'` serves fonts from `/_nuxt/my-fonts` rather than from `/my-fonts`. It remains a public path in development and with the webpack and rspack builders.

#### `global: true` families get font fallback metrics

A family declared with [`global: true`](https://nuxt.com/modules/fonts/configuration#global) previously got no fallback metrics at all, which made the fonts most likely to cause layout shift the ones Nuxt Fonts did least about. Fallbacks are now generated wherever Nuxt Fonts can see the family being used, so `font-family: 'Anton'` in your CSS becomes `font-family: 'Anton', 'Anton Fallback: Arial', ...` with the matching metric-override rules alongside it, exactly as it would for a family that wasn't declared globally. Usage Nuxt Fonts can't scan (an SVG, or a family chosen at runtime) is unaffected and continues to rely on the global stylesheet alone.

The `@font-face` rule for the family itself still comes from the global stylesheet and isn't duplicated, and preload hints are unchanged. If you don't want the fallback rules, set [`fallbacks`](https://nuxt.com/modules/fonts/configuration#fallbacks) to `[]` for the family.

#### Font metadata is cached per project

Font metadata and downloaded font files are now cached in `node_modules/.cache/nuxt/fonts/meta` relative to your project root rather than to the directory you run Nuxt from. The first build after upgrading will re-resolve and re-download fonts, and you can now configure the location with the new [`cache`](https://nuxt.com/modules/fonts/configuration#cache) option.

#### Injected `@font-face` rules are minified with lightningcss

Generated `@font-face` declarations were previously minified with esbuild unless you had opted into `css.lightningcss`. They are now minified with lightningcss, so the exact serialisation of the CSS we inject may differ (for example `local(Font Name)` rather than `local("Font Name")`). This is cosmetic, but it will show up in snapshot tests.

lightningcss is an optional peer dependency, as its native binary is several megabytes. Nuxt v4.5 (with Vite 8) brings this in, but if you are using an earlier version of Nuxt, you will need to install it manually to enable css minification.

```bash [Terminal]
npm install --save-dev lightningcss
```

#### Font failures fail production builds

[`throwOnError`](https://nuxt.com/modules/fonts/configuration#throwonerror) now defaults to `true` outside of dev mode, so rather than producing a build with a missing font, the build fails when no provider can serve a family, when a font file cannot be downloaded after retries, or when you set `provider` on a family and that provider does not contain it.

A single provider failing is not enough on its own, as resolution carries on to the next one.

A family that cannot be found by any provider still warns rather than failing, as does an unknown provider name, so fonts you declare yourself in CSS are unaffected.

Set `throwOnError: false` to restore the previous behaviour.

#### Family-level `@font-face` descriptors now apply to provider fonts

`display` and `unicodeRange` set on a family were previously only honoured for families you declared manually with `src`, and were silently dropped for fonts resolved from a provider. They now apply in both cases. Descriptors other than `display`, `weight` and `style` (such as `stretch`, `featureSettings` and `variationSettings`) are also no longer dropped from manually declared families.

If you set any of these options on a family and (perhaps unknowingly) relied on them being ignored, your generated CSS will change. Note that setting `unicodeRange` on a family marks it as subsetted, so it is no longer preloaded by default; set [`preload`](https://nuxt.com/modules/fonts/configuration#preload) explicitly if you still want a preload link for it.

#### The `local` provider only scans layer `public/` directories

The [`local` provider](https://nuxt.com/modules/fonts/providers#local) previously scanned every directory in `nitro.publicAssets`, including those that modules register for their own files. It now scans only the `public/` directory of your project and of each layer. If you relied on fonts in another `nitro.publicAssets` directory, add it to [`local.dirs`](https://nuxt.com/modules/fonts/providers#fonts-installed-from-npm):

```ts
export default defineNuxtConfig({
  fonts: {
    local: {
      dirs: ['assets/fonts'],
    },
  },
})
```

### Deprecations

#### `experimental.processCSSVariables`

`fonts.experimental.processCSSVariables` has moved to `fonts.processCSSVariables`. The old location still works and now warns.

```ts
export default defineNuxtConfig({
  fonts: {
    processCSSVariables: true,
  },
})
```

#### `fonts:public-asset-context`

Use [`fonts:resolved`](https://nuxt.com/modules/fonts/advanced#fontsresolved) instead. It reports each family as it is resolved, with the URL every font file is served from and a `readFont()` to read its contents, so there is no need to reconstruct paths from the asset context. The old hook still works.

### New Features

#### `@font-face` rules are injected with the webpack and rspack builders

Font injection now works with `@nuxt/webpack-builder` and `@nuxt/rspack-builder`. Previously our CSS transform ran after `css-loader` had already turned your stylesheets into JavaScript modules, so no `@font-face` rule was ever injected, and a family declared with `global: true` failed the client build.

There's nothing to change on your side, but a build that silently shipped no web fonts now downloads them and serves them from `/_fonts`. See [builder support](https://nuxt.com/modules/fonts/advanced#builder-support).

#### Subsetting fonts to the glyphs you use

[`glyphs`](https://nuxt.com/modules/fonts/configuration#glyphs) reduces every font file we emit to the characters needed to render the text you give it, which can dramatically cut the bytes shipped for icon fonts and single-language sites.

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      glyphs: 'Handgloves & 0123',
    },
  },
})
```

Where a provider can subset server-side the characters are passed through to it, so the full file is never downloaded. Every other file is subsetted after download, which needs the [`subset-font`](https://github.com/papandreou/subset-font) package; we will offer to install it the first time you run Nuxt with `glyphs` set, or warn where we cannot ask, such as in CI, and fail the build before downloading any font that needs subsetting locally.

#### Variable font axes

[`variableAxis`](https://nuxt.com/modules/fonts/configuration#variableaxis) chooses the values a variable font is shipped at, for any OpenType axis rather than just `wght`. Pin an axis to a single value, or narrow it to a range.

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      variableAxis: {
        CASL: [1],
        MONO: [{ min: 0, max: 1 }],
      },
    },
  },
})
```

#### Named font weights

[`weights`](https://nuxt.com/modules/fonts/configuration#weights) accepts CSS keywords as well as numbers, and the [`local` provider](https://nuxt.com/modules/fonts/providers#local) recognises them in filenames, so `MyFont-Bold.woff2` is picked up as weight `700`. Remote providers still expect numeric weights.

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      weights: ['medium', 'semibold'],
    },
  },
})
```

#### Scanning additional local font directories

The [`local` provider](https://nuxt.com/modules/fonts/providers#local) can scan directories beyond `public/`, and reports the filenames it looked for when a family cannot be resolved.

```ts
export default defineNuxtConfig({
  fonts: {
    local: {
      dirs: ['assets/fonts'],
    },
  },
})
```

#### Preloading fonts by subset

The `preload` option (on `defaults` and on individual `families`) now accepts `{ subsets: [...] }` or a filter function, so you can preload just the subsets your app needs.

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      preload: { subsets: ['latin'] },
    },
  },
})
```

See [`preload`](https://nuxt.com/modules/fonts/configuration#preload) for the full set of values.

#### Configurable cache

You can now point the font cache at a directory of your choice, pass your own [unstorage](https://unstorage.unjs.io/) instance, or disable persistent caching with `cache: false`.

```ts
export default defineNuxtConfig({
  fonts: {
    cache: '.cache/fonts',
  },
})
```

#### Custom CSS variable prefixes

[`processCSSVariables`](https://nuxt.com/modules/fonts/configuration#processcssvariables) now accepts a custom prefix, so `processCSSVariables: 'my-app'` will process `--my-app-*` variables only.

#### Reusing resolved fonts from another module

The new [`fonts:resolved`](https://nuxt.com/modules/fonts/advanced#fontsresolved) hook reports each family as it is resolved, with the URL every font file is served from and a `readFont()` to read its contents. Modules that need the fonts your app already uses, to render an OG image or a PDF, can reuse them rather than resolving and downloading their own.

```ts
nuxt.hook('fonts:resolved', (font) => {
  for (const file of font.files) {
    // file.url, await file.readFont()
  }
})
```

## Upgrading to v0.14

### Breaking Changes

#### Default font format is now `woff2` only

Previously, font providers could return multiple formats (e.g., `woff2`, `woff`, `truetype`). The default behavior now only resolves `woff2` format fonts, which is universally supported in all modern browsers.

This means your rendered `@font-face` declarations will typically have fewer `src` entries, reducing overall CSS size.

The Google Fonts provider previously also downloaded a legacy-format copy of each font that contained every script the font supports, regardless of the configured [`subsets`](https://nuxt.com/modules/fonts/configuration#subsets). Text in a script outside those subsets, such as Arabic, now renders in a fallback font. To keep it in your font, add its subset:

```ts
export default defineNuxtConfig({
  fonts: {
    families: [
      { name: 'Cairo', subsets: ['arabic', 'latin', 'latin-ext'] },
    ],
  },
})
```

If you need to support legacy browsers that require other formats, you can configure this in your `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      formats: ['woff2', 'woff', 'ttf'],
    },
  },
})
```

The available format values are: `'woff2'`, `'woff'`, `'ttf'`, `'otf'`, `'eot'`.

### New Features

#### Font format resolution

You can now control which font formats are resolved via the new `defaults.formats` option. This defaults to `['woff2']`.

```ts
export default defineNuxtConfig({
  fonts: {
    defaults: {
      formats: ['woff2'],
    },
  },
})
```

#### Provider-specific font family options

You can now pass provider-specific options when configuring individual font families using the new `providerOptions` property:

```ts
export default defineNuxtConfig({
  fonts: {
    families: [
      {
        name: 'My Font',
        provider: 'google',
        providerOptions: {
          google: {
            experimental: {
              variableAxis: {
                wdth: [['75', '100']],
              },
            },
          },
        },
      },
    ],
  },
})
```

#### `throwOnError` option

You can now configure whether font resolution errors should throw or just warn:

```ts
export default defineNuxtConfig({
  fonts: {
    throwOnError: true, // default: false
  },
})
```


## Sitemap

See the full [sitemap](https://nuxt.com/sitemap.md) for all pages.
