Getting Started

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 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:

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.

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 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 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 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 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.

Terminal
npm install --save-dev lightningcss

Font failures fail production builds

throwOnError now defaults to true outside of dev mode, so rather than producing a build with a missing font, the build fails when a provider errors, 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 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 explicitly if you still want a preload link for it.

Deprecations

experimental.processCSSVariables

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

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

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.

Subsetting fonts to the glyphs you use

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.

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 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 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.

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

Named font weights

weights accepts CSS keywords as well as numbers, and the local provider recognises them in filenames, so MyFont-Bold.woff2 is picked up as weight 700. Remote providers still expect numeric weights.

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

Scanning additional local font directories

The local provider can scan directories beyond public/, and reports the filenames it looked for when a family cannot be resolved.

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.

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

See 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 instance, or disable persistent caching with cache: false.

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

Custom CSS variable prefixes

processCSSVariables now accepts a custom prefix, so processCSSVariables: 'my-app' will process --my-app-* variables only.

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. In most cases this is a transparent improvement and requires no action.

If you need to support legacy browsers that require other formats, you can configure this in your nuxt.config.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'].

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:

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:

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