Font Converter

Creating NPM Font Packages

Learn how to create, structure, and publish NPM packages for fonts. Enable easy font distribution, versioning, and integration with modern JavaScript frameworks like React and Next.js.

TL;DR - Key Takeaways

  • • Use WOFF2 format for smallest package size (70-90% smaller than TTF)
  • • Export separate CSS files for each font weight to enable tree-shaking
  • • Follow semantic versioning—major for breaking changes, minor for new weights
  • • Consider Fontsource pattern for compatibility with existing ecosystem

Share this page to:

Distributing fonts via NPM packages offers significant advantages over traditional approaches. Developers can install fonts as dependencies, lock specific versions, and benefit from bundler integration for optimal loading. This is particularly powerful for design systems and component libraries where typography consistency is critical.

This guide covers creating custom font packages from scratch, following established patterns like Fontsource, and publishing to NPM or private registries. Whether you're packaging custom brand fonts or creating reusable font utilities, these techniques ensure professional distribution.

The key benefits include version-controlled font updates, simplified installation via package managers, integration with bundlers like Webpack and Vite, and the ability to include font metadata and CSS alongside font files.

Package Structure

A well-organized font package separates font files, CSS, and documentation for flexibility.

Recommended Structure

@company/font-inter/
├── package.json
├── README.md
├── LICENSE
├── index.css           # All weights combined
├── files/
│   ├── inter-latin-400-normal.woff2
│   ├── inter-latin-400-italic.woff2
│   ├── inter-latin-500-normal.woff2
│   ├── inter-latin-600-normal.woff2
│   └── inter-latin-700-normal.woff2
├── 400.css             # Only 400 weight
├── 500.css             # Only 500 weight
├── 600.css             # Only 600 weight
├── 700.css             # Only 700 weight
└── variable.css        # Variable font (if available)

Individual Weight CSS

Separate CSS files for each weight enable tree-shaking—only import what you use.

// Only import needed weights
import '@company/font-inter/400.css';
import '@company/font-inter/700.css';

Combined Index CSS

A single import for all weights when bundle size isn't a concern.

// Import all weights
import '@company/font-inter';
// or
import '@company/font-inter/index.css';

package.json Configuration

Proper package.json configuration ensures compatibility with bundlers and correct file resolution.

{
  "name": "@company/font-inter",
  "version": "1.0.0",
  "description": "Inter font family for web",
  "main": "index.css",
  "style": "index.css",
  "files": [
    "files/",
    "*.css"
  ],
  "exports": {
    ".": "./index.css",
    "./400.css": "./400.css",
    "./500.css": "./500.css",
    "./600.css": "./600.css",
    "./700.css": "./700.css",
    "./variable.css": "./variable.css"
  },
  "keywords": ["font", "inter", "webfont", "woff2"],
  "license": "OFL-1.1",
  "repository": {
    "type": "git",
    "url": "https://github.com/company/fonts"
  },
  "sideEffects": ["*.css"]
}

Important: sideEffects

The sideEffects field tells bundlers that CSS files should not be tree-shaken even if no exports are used. Without this, bundlers might incorrectly remove your CSS.

CSS @font-face Structure

Each CSS file should include optimized @font-face declarations with proper font-display and format specification.

400.css

/* Inter Latin 400 Normal */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url('./files/inter-latin-400-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
    U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
    U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

Unicode Range

Include unicode-range for subsetting. Browsers only download fonts when characters in that range are needed.

font-display: swap

Ensures text is immediately visible with a fallback font, then swaps to the custom font when loaded.

Variable Font Support

Variable fonts allow a single file to contain all weights, reducing HTTP requests and total download size.

Variable Font CSS

/* variable.css */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('./files/inter-latin-variable.woff2') format('woff2-variations');
}

/* Optional: CSS custom property for weight */
:root {
  --font-inter: 'Inter Variable', 'Inter', system-ui, sans-serif;
}

Variable Font Naming

Use a distinct font-family name for variable fonts (e.g., "Inter Variable") so developers can explicitly choose between static and variable versions based on browser support needs.

Publishing to NPM

Follow these steps to publish your font package to NPM or a private registry.

1. Verify Package Contents

# Preview what will be published
npm pack --dry-run

# Check package size
npm pack && ls -la *.tgz

2. Version and Tag

# Bump version
npm version patch  # 1.0.0 -> 1.0.1
npm version minor  # 1.0.0 -> 1.1.0 (new weights)
npm version major  # 1.0.0 -> 2.0.0 (breaking changes)

3. Publish

# Public package
npm publish --access public

# Scoped private package
npm publish

# Private registry
npm publish --registry https://registry.company.com

Usage in Projects

Once published, developers can install and use your font package easily.

Next.js / React

// app/layout.tsx or _app.tsx
import '@company/font-inter/400.css';
import '@company/font-inter/700.css';

// tailwind.config.js
module.exports = {
  theme: {
    fontFamily: {
      sans: ['Inter', 'system-ui'],
    },
  },
}

Vite / Plain JS

// main.js or main.ts
import '@company/font-inter';

// styles.css
body {
  font-family: 'Inter', sans-serif;
}

Convert Fonts for NPM Distribution

Convert your fonts to WOFF2 format optimized for NPM package distribution.

Start Converting Fonts
Sarah Mitchell

Written & Verified by

Sarah Mitchell

Product Designer, Font Specialist

NPM Font Packages FAQs

Common questions about creating and distributing font packages