Skip to content

How It Works ​

This page describes what the plugin does inside Vite: when it generates fonts, where they go, and how options change that. Font generation itself is handled by the webfont generator engine. The plugin calls it whenever fonts need to be built or rebuilt.

Overview ​

When Vite starts, in either development or build, the plugin:

  1. Resolves your options and finds the SVG files matching files (or each design's files in variants) inside context.
  2. Checks the output cache. If nothing changed since the last run, it reuses the stored fonts and CSS instead of calling the engine.
  3. Otherwise calls the engine to generate every format listed in types, and stores the result in the cache for next time. The engine keeps its own persistent cache, so unchanged icons and formats are reused even then.
  4. Exposes the CSS through the virtual module, virtual:vite-svg-2-webfont.css by default (see moduleId). Importing it brings the @font-face rule and icon classes into your app.

What happens next depends on whether you are running the dev server or a production build.

Development ​

  • Fonts stay in memory. The dev server serves each format at /<fontName>.<type>, for example /iconfont.woff2, and the virtual CSS points at those URLs. Nothing is written to disk unless you enable generateFiles.
  • SVG changes update the page without a full reload. The plugin watches your icon folders. When an SVG is added, changed, or removed, it rebuilds the font and reloads the virtual CSS module through Vite's HMR, so icons update in place.
  • Rebuilds are incremental. In development the plugin asks the engine to keep its work between builds, so a rebuild only reprocesses the icons that changed. It falls back to a full rebuild if an incremental one fails.
  • Rebuilds are fast by default. In development WOFF2 uses Brotli quality 10 instead of 11: the font renders identically, its bytes are a little larger, and encoding is about twice as fast. Set woff2CompressionQuality to pin one value for both.
  • Edits are serialized. Changes that arrive close together (within about 25 ms) are batched into one rebuild, and rebuilds run one at a time in order. If an edit produces invalid input, the plugin reports a warning and keeps serving the last good font until a later change succeeds.

What the watcher sees ​

SetupWatched
Single design with flat globs, such as the default *.svgThe context folder itself
Single design with nested globs, such as **/*.svgThe context folder recursively, including subfolders created while running
Multi-weight (variants)Each design's folder, recursively, including subfolders created while running

Only .svg files trigger rebuilds. In recursively watched folders, SVGs that your globs don't match are ignored. Changes to a custom cssTemplate or htmlTemplate and to vite.config.ts itself need a dev server restart.

Build ​

  • Fonts become ordinary Vite assets. The plugin writes the fonts to a temporary folder and points the virtual CSS at them. Vite then processes the fonts like any other asset referenced from CSS: hashed file names, build.assetsDir, base, and assetFileNames all apply. See production font output.
  • Vite's assetsInlineLimit still applies. Small fonts may be embedded as data URLs unless you set build.assetsInlineLimit: 0.
  • Preload tags are added to built HTML. With preloadFormats, the plugin adds <link rel="preload" as="font" crossorigin> for the emitted fonts, filtered by shouldProcessHtml.
  • The plugin writes no files of its own unless you enable both generateFiles and allowWriteFilesInBuild. Those files are extra copies; Vite's bundled assets are unaffected.
  • Other plugins can read the emitted font URLs through the public API.
  • WOFF2 uses Brotli quality 11 (the smallest output) unless you set woff2CompressionQuality.

How options change the flow ​

OptionDevelopmentBuild
inlineFonts are embedded in the CSS as base64; no font URLs are servedFonts are embedded in the CSS; no font assets or preload tags
generateFilesWrites the selected files (CSS, HTML, fonts) to disk, rewriting CSS/HTML only when their content changesIgnored unless allowWriteFilesInBuild is set
allowWriteFilesInBuild—Lets generateFiles write to dest, cssDest, and htmlDest
preloadFormats—Adds preload tags to built HTML (not with inline)
cssContextTurns off incremental rebuilds (every change is a full rebuild) and the output cacheTurns off the output cache
woff2CompressionQualityDefaults to 10 when unsetDefaults to 11 when unset
cssFontsUrlAffects files written by generateFilesAffects files written by generateFiles; bundled CSS URLs come from Vite
moduleIdName of the virtual module to importSame

Every other option, such as types, classPrefix, or normalize, is passed to the engine and affects the generated fonts and CSS the same way in both modes.

Caching ​

Two caches, both on by default and both stored in Vite's cacheDir (node_modules/.vite by default), make restarts and repeated builds fast. Set cache: false to turn both off: every run then generates from scratch, and the plugin writes nothing to cacheDir.

node_modules/.vite/vite-svg-2-webfont/
├── iconfont.json            # output cache manifest: cache keys, CSS, HTML
├── iconfont-3f2a….woff2     # output cache fonts, named by a hash of their content
├── iconfont-91bc….woff
└── engine/                  # engine cache
    └── iconfont.….wfcache

Output cache ​

The plugin keeps a persistent cache of its output, so restarting the dev server or re-running a build with unchanged icons skips font generation entirely. Development and build results are stored separately, so switching between vite and vite build doesn't evict either one. Font files that are no longer referenced are removed when a new result is stored.

The cache key covers everything that can change the output:

  • the contents of every input SVG, and of custom cssTemplate and htmlTemplate files
  • the resolved plugin options
  • the engine version
  • whether this is a development or build run, and whether inline is enabled

If any of these change, the plugin calls the engine again and replaces the stored result. The cache is checked only on the first generation when Vite starts; rebuilds triggered by file changes in development always use the engine.

The output cache is skipped when:

  • cssContext is set, because a callback can change the output in ways the key can't capture
  • an input file can't be read when the key is computed

Engine cache ​

Whenever the plugin does call the engine, the engine reuses its own persistent cache, so a changed option or an edited icon doesn't start from nothing:

  • unchanged icons skip parsing and path processing; an edited icon is reprocessed alone
  • formats whose inputs didn't change, such as every font after a CSS-only option change, come straight from the cache
  • it also applies with cssContext, where the output cache can't

See cacheLocation for the engine's details and performance for measured speedups.

In development, rebuilds triggered by file changes normally use the engine's in-memory incremental generation instead, which doesn't update the engine cache: icons edited during a session are reprocessed once on the next start. A rebuild calls the engine with its cache, like the first generation, when incremental generation isn't available: with cssContext, for the first change after an output cache hit, or after an incremental rebuild fails.

Failures and clearing ​

Both caches are optimizations only. If the cache folder can't be written, generation still succeeds, and a damaged or missing entry is treated as a miss. Output is identical with and without them.

To force a fresh generation, delete the vite-svg-2-webfont folder inside Vite's cacheDir. The plugin recreates it on the next run.

Multiple plugin instances

Entries are stored per fontName. If you register the plugin more than once, give each instance its own fontName. Otherwise the instances keep replacing each other's cache entries and never get a cache hit.

See also ​

Released under the MIT License.