vite-font-extractor-plugin

March 24, 2026 · View on GitHub

vite-font-extractor-plugin

npm version license CI downloads

vite-font-extractor-plugin

Vite plugin that extracts only the glyphs you use from font files — icon fonts, text fonts, or both. Supports Vite 5, 6, 7, and 8.

Before:  Material Icons   348 KB (all 2,000+ icons)
After:   Material Icons    12 KB (only 3 icons you need)   → 97% smaller

Features

  • Icon font minification — keep only the ligatures you use (Material Icons, FontAwesome, etc.)
  • Text font subsetting — keep only specific characters via ?subset= query
  • Zero-config auto mode — detects glyphs from CSS content: "..." automatically
  • Google Fonts optimization — appends &text= parameter for server-side subsetting
  • Works in build and dev — minifies fonts on-the-fly during development
  • Vite 5–8 support — compatible with both Rollup and Rolldown bundlers
  • Content-based hashing — output filenames change when config changes (cache-safe)
  • Live playground — see the plugin in action
  • Disk cache — skip re-minification on repeated builds

Quick Start

npm install vite-font-extractor-plugin

Zero-config (auto mode)

// vite.config.js
import FontExtractor from 'vite-font-extractor-plugin'

export default defineConfig({
    plugins: [FontExtractor()],
})

The plugin scans all CSS for content: "..." declarations, collects referenced glyphs, and strips everything else from font files.

Manual mode

Specify exactly which icons to keep:

FontExtractor({
    type: 'manual',
    targets: [
        {
            fontName: 'Material Icons',
            ligatures: ['close', 'menu', 'search', 'home'],
        },
    ],
})

Text Font Subsetting

Strip unused characters from text fonts like Roboto, Inter, or Open Sans.

Via CSS ?subset= query

/* Keep only Latin letters and digits */
@font-face {
    font-family: 'Roboto';
    src: url('./fonts/Roboto.woff2?subset=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') format('woff2');
}

/* Keep a Unicode range (e.g. Cyrillic) */
@font-face {
    font-family: 'Roboto';
    src: url('./fonts/Roboto.woff2?subset=U+0400-04FF') format('woff2');
}

/* Combine characters and Unicode ranges */
@font-face {
    font-family: 'Roboto';
    src: url('./fonts/Roboto.woff2?subset=ABC,U+0400-04FF') format('woff2');
}

Via JS import

Useful for runtime font loading (Rive, Canvas, PDF generators):

import fontUrl from './fonts/Roboto.woff2?subset=ABCabc'

// fontUrl is a clean URL to the subsetted font asset
rive.load({fonts: [fontUrl]})

Via plugin config

FontExtractor({
    type: 'manual',
    targets: [
        {
            fontName: 'Roboto',
            characters: 'ABCabc0123456789',
            engine: 'subset',
        },
    ],
})

Google Fonts

The plugin appends &text= to Google Font URLs, letting Google's servers do the subsetting:


<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
@import "https://fonts.googleapis.com/icon?family=Material+Icons";

Multi-family URLs are supported: family=Material+Icons|Roboto.

Caching

Enable disk cache to skip re-minification when fonts and config haven't changed:

FontExtractor({
    type: 'manual',
    targets: [{fontName: 'Material Icons', ligatures: ['close']}],
    cache: true,              // caches in node_modules/.font-extractor-cache
    // cache: './my-cache',   // or a custom path
})

The cache is automatically cleaned when cache is set to false.

Vite Compatibility

ViteStatus
v5Stable
v6Stable
v7Stable
v8Experimental

Vite 8 uses Rolldown instead of Rollup. Icon font minification works fully. The ?subset= feature has known limitations — see ROADMAP for details.

API Reference

import FontExtractor from 'vite-font-extractor-plugin'

FontExtractor(options?: PluginOption): Plugin

PluginOption

OptionTypeDefaultDescription
type'auto' | 'manual''auto'Glyph detection strategy
targetsTarget | Target[]Fonts to process. Required in manual mode
cacheboolean | stringEnable disk cache (or custom path)
logLevel'info' | 'warn' | 'error' | 'silent'Vite configLog verbosity
apply'build' | 'serve'bothRestrict to build or dev mode
ignorestring[]Font names to skip entirely

Target

OptionTypeDescription
fontNamestringMust match font-family in CSS (without quotes)
ligaturesstring[]Icon names to keep (e.g. ['close', 'menu'])
rawsstring[]Raw Unicode characters to keep
charactersstringCharacters string for text font subsetting
unicodeRangesstring[]Unicode ranges (e.g. ['U+0400-04FF'])
engine'icon' | 'subset'icon for icon fonts, subset for text fonts
withWhitespacebooleanInclude whitespace glyphs (default: false)

Troubleshooting

Font not being minified?

  • Check that fontName exactly matches the font-family value in your CSS @font-face (without quotes)
  • Make sure the font isn't in the ignore list

Warning: "has no minify options"?

  • The plugin found a @font-face but the font isn't in targets
  • Fonts with ?subset= URLs don't trigger this warning — they're processed automatically
  • To silence: add the font to targets, use ?subset=, or add to ignore

Google Font URL not transformed?

  • Use spaces in fontName: 'Material Icons', not 'Material+Icons'

Auto mode missing glyphs?

  • Auto mode only detects glyphs from CSS content: "..." properties
  • If icons are referenced by class name or JS, use manual mode instead

License

MIT

Contributing

Issues and pull requests are welcome on GitHub.