npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

persian-fontkit

v1.0.2

Published

Automatic Persian (Farsi) web font optimizer for Next.js and React with smart caching, subsetting, preloading, and SSR support

Downloads

13

Readme

🎨 Persian FontKit

npm version License: MIT TypeScript

Automatic Persian (Farsi) web font optimizer for Next.js and React

Reduce font file sizes by 60-80% • Improve Core Web Vitals • Zero Config

FeaturesInstallationUsageExamplesAPI


🧩 Problem this Solves

Persian fonts like Vazir, IRANSans, Shabnam, Yekan, and Kalameh are typically 2-4 MB in size, causing:

  • ⏱️ Slow page load times
  • 📉 Poor Core Web Vitals scores
  • 💸 Higher bandwidth costs
  • 😞 Bad user experience

persian-fontkit automatically:

✅ Subsets unused glyphs (60-80% size reduction)
✅ Generates optimized .woff2 files
✅ Creates CSS with font-display: swap
✅ Adds preload tags for faster loading
✅ Provides React hooks for dynamic loading
✅ Integrates seamlessly with Next.js


⚡ Features

🔧 CLI Tool

  • Optimize entire font directories with one command
  • Support for .ttf, .otf, .woff, .woff2 formats
  • Automatic subset generation (Persian + Latin + Numbers)
  • Hash-based cache-busting filenames
  • Smart caching system - Skip re-optimization of unchanged fonts (~100x faster)
  • Beautiful CLI output with progress tracking

⚛️ React Integration

  • usePersianFont hook for client-side dynamic font loading
  • SSR-compatible (doesn't break server rendering)
  • Automatic <link rel="preload"> injection (client-side)
  • TypeScript support with full type definitions
  • Note: For true SSR, use static preload in layout.tsx (see below)

🚀 Next.js Plugin

  • Automatic font optimization during build
  • Zero-config setup for Next.js 13, 14, and 15
  • App Router and Pages Router support
  • Works with next/font/local patterns

📦 Installation

npm install persian-fontkit
# or
yarn add persian-fontkit
# or
pnpm add persian-fontkit

🚀 Quick Start

1. CLI Usage

Optimize all fonts in a directory:

npx persian-fontkit optimize ./public/fonts --output ./dist/fonts

Output:

🚀 Persian FontKit Optimizer

Input:  /path/to/public/fonts
Output: /path/to/dist/fonts

📁 Scanning for font files...
✓ Found 2 font file(s)

[1/2] Optimizing vazir-regular.ttf...
  Original:  2.4 MB
  Optimized: 580 KB
  ✓ Reduced by 75.8%

[2/2] Optimizing vazir-bold.ttf...
  Original:  2.5 MB
  Optimized: 620 KB
  ✓ Reduced by 75.2%

✨ Optimization complete!

📊 Summary:
  Total fonts processed: 2
  Original size:  4.9 MB
  Optimized size: 1.2 MB
  Total saved:    3.7 MB
  Size reduction: 75.5%

2. React Hook Usage (Client-Side)

"use client"; // ⚠️ Required: Hook only works in client components

import { usePersianFont } from "persian-fontkit/hooks";

export default function MyComponent() {
  // Loads fonts dynamically after page hydration
  usePersianFont({
    family: "Vazir",
    weight: [400, 700],
    subsets: ["farsi", "latin"],
    basePath: "/fonts",
  });

  return (
    <div style={{ fontFamily: "Vazir" }}>
      <h1>سلام دنیا</h1>
      <p>Hello World</p>
    </div>
  );
}

Note: This hook runs client-side only (using useEffect). For SSR, see Next.js Integration.

3. Next.js Integration

Option A: Static Preload (Recommended for SSR) ✨

For true server-side rendering with optimal performance:

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="fa" dir="rtl">
      <head>
        {/* ✅ This runs on the server - true SSR */}
        <link
          rel="preload"
          href="/fonts/vazir-400.woff2"
          as="font"
          type="font/woff2"
          crossOrigin="anonymous"
        />
        <style>{`
          @font-face {
            font-family: 'Vazir';
            src: url('/fonts/vazir-400.woff2') format('woff2');
            font-display: swap;
          }
        `}</style>
      </head>
      <body>{children}</body>
    </html>
  );
}

Option B: Client-Side Hook (For Dynamic Loading) ⚡

For client-side dynamic font loading (after page hydration):

"use client";

import { usePersianFont } from "persian-fontkit/hooks";

export default function MyComponent() {
  // ⚠️ This runs only in the browser (useEffect)
  usePersianFont({
    family: "Vazir",
    weight: [400, 700],
    basePath: "/fonts",
  });

  return <div style={{ fontFamily: "Vazir" }}>محتوای شما</div>;
}

Option C: Next.js Plugin (Build-Time Optimization)

// next.config.js
const { withPersianFonts } = require("persian-fontkit/plugin");

module.exports = withPersianFonts({
  fonts: [{ family: "Vazir", weights: [400, 700], subsets: ["farsi"] }],
});

� SSR vs Client-Side: Which Should I Use?

✅ Use Static Preload (Option A) when:

  • You want true server-side rendering
  • First paint performance is critical
  • You know your fonts at build time
  • SEO and Core Web Vitals are important
  • Recommended for production apps

⚡ Use Client-Side Hook (Option B) when:

  • You need dynamic font loading based on user preferences
  • Loading fonts conditionally (e.g., language switcher)
  • Building interactive demos or playgrounds
  • Font selection happens at runtime

🎯 Technical Details:

| Feature | Static Preload | Client Hook | | --------------- | ------------------ | ------------------- | | Execution | Server-side | Client-side only | | Load Time | Before first paint | After hydration | | SSR Support | ✅ True SSR | ⚠️ SSR-compatible* | | Performance | Faster FCP/LCP | Slower initial load | | Use Case | Production apps | Dynamic scenarios |

*SSR-compatible means it won't break during server rendering (safe window check), but actual font loading happens only in the browser.


�📚 Usage Examples

CLI Options

# Basic usage
npx persian-fontkit optimize ./fonts --output ./dist

# Custom subsets
npx persian-fontkit optimize ./fonts --subsets farsi latin numbers

# Different format
npx persian-fontkit optimize ./fonts --format woff

# Disable hash in filenames
npx persian-fontkit optimize ./fonts --no-hash

# Custom CSS filename
npx persian-fontkit optimize ./fonts --css custom-fonts.css

# Disable caching (re-optimize all fonts)
npx persian-fontkit optimize ./fonts --no-cache

# Custom cache directory
npx persian-fontkit optimize ./fonts --cache-dir ./.my-cache

# View cache statistics
npx persian-fontkit cache --stats

# Clear cache
npx persian-fontkit cache --clear

# Show help
npx persian-fontkit --help

React Hook Options

import { usePersianFont } from "persian-fontkit/hooks";

function MyComponent() {
  usePersianFont({
    family: "Vazir", // Font family name
    weight: [400, 700], // Font weights to load
    subsets: ["farsi", "latin"], // Character subsets
    basePath: "/fonts", // Base path for font files
    display: "swap", // font-display property
    preload: true, // Add preload links
    fallback: ["Tahoma", "Arial"], // Fallback fonts
  });

  return <div>Your content</div>;
}

Programmatic API

import { optimizeFont, optimizeFonts, getGlobalCache } from "persian-fontkit";

// Optimize a single font (cache enabled by default)
const result = await optimizeFont({
  inputPath: "./fonts/vazir-regular.ttf",
  outputDir: "./dist/fonts",
  fontFamily: "Vazir",
  fontWeight: 400,
  subsets: ["farsi", "latin"],
  format: "woff2",
  cache: true, // Default: true
});

console.log(`Reduced by ${result.reduction}%`);

// Disable cache for specific optimization
const result2 = await optimizeFont({
  inputPath: "./fonts/special.ttf",
  outputDir: "./dist/fonts",
  cache: false, // Disable caching
});

// Optimize multiple fonts
const results = await optimizeFonts(
  ["./fonts/vazir-400.ttf", "./fonts/vazir-700.ttf"],
  "./dist/fonts",
  { subsets: ["farsi", "latin"] }
);

// Cache management
const cache = getGlobalCache();

// Get cache statistics
const stats = await cache.getStats();
console.log(`Cache entries: ${stats.entries}`);
console.log(`Total size: ${stats.totalSize} bytes`);

// Clear cache
await cache.clear();

// Clean old entries (older than 7 days)
const deletedCount = await cache.cleanOld(7 * 24 * 60 * 60 * 1000);
console.log(`Deleted ${deletedCount} old entries`);

🎯 Supported Subsets

| Subset | Unicode Range | Description | | ------------- | ------------------------ | --------------------------- | | farsi | U+0600-06FF, U+FB50-FDFF | Persian & Arabic characters | | latin | U+0020-007E, U+00A0-00FF | Basic Latin characters | | numbers | U+0030-0039, U+06F0-06F9 | Latin & Persian digits | | punctuation | U+0020-002F, U+060C-061F | Common punctuation |

Default: All subsets are included by default.


🎨 Popular Persian Fonts

Persian FontKit works with all Persian fonts, including:

  • Vazir (Vazirmatn)
  • IRANSans (IRANSansX)
  • Shabnam
  • Yekan
  • Kalameh
  • Samim
  • Sahel
  • Tanha
  • Parastoo
  • Gandom

📊 Performance Comparison

Before Optimization

vazir-regular.ttf: 2.4 MB
vazir-bold.ttf:    2.5 MB
Total:             4.9 MB
Load time:         ~3.2s (3G)

After Optimization

vazir-regular.woff2: 580 KB ⚡
vazir-bold.woff2:    620 KB ⚡
Total:               1.2 MB
Load time:           ~0.8s (3G)

Result: 75% smaller, 4x faster! 🚀


🔧 Configuration

CLI Config File

Create persian-fonts.config.js:

module.exports = {
  fonts: [
    {
      family: "Vazir",
      weights: [400, 700],
      subsets: ["farsi", "latin", "numbers"],
    },
    {
      family: "IRANSans",
      weights: [400, 500, 700],
      subsets: ["farsi"],
      src: "iransans/*.ttf", // Optional: specific files
    },
  ],
  sourceDir: "./public/fonts",
  outputDir: "./public/fonts/optimized",
  format: "woff2",
  verbose: true,
};

Then run:

npx persian-fontkit optimize

📖 API Reference

optimizeFont(options)

Optimize a single font file.

Options:

  • inputPath (string): Path to input font file
  • outputDir (string): Output directory
  • fontFamily (string, optional): Font family name
  • fontWeight (number, optional): Font weight (default: 400)
  • fontStyle ('normal' | 'italic', optional): Font style
  • subsets (string[], optional): Character subsets to include
  • format ('woff2' | 'woff' | 'ttf', optional): Output format
  • fontDisplay (string, optional): CSS font-display property
  • useHash (boolean, optional): Add hash to filename

Returns: Promise<OptimizationResult>

usePersianFont(options)

React hook for client-side dynamic font loading.

⚠️ Important: This hook uses useEffect and only runs in the browser (after hydration). It won't execute during server-side rendering. For true SSR, use static <link> tags in your layout component.

Options:

  • family (string): Font family name
  • weight (number | number[]): Font weights
  • subsets (string[], optional): Character subsets
  • basePath (string, optional): Base path for fonts
  • display (string, optional): font-display property
  • preload (boolean, optional): Add preload links (client-side)
  • fallback (string[], optional): Fallback fonts

withPersianFonts(config, nextConfig)

Next.js plugin wrapper.

Config:

  • fonts (FontConfig[]): Array of font configurations
  • sourceDir (string, optional): Source directory
  • outputDir (string, optional): Output directory
  • format (string, optional): Output format
  • verbose (boolean, optional): Enable logging

🏗️ Project Structure

persian-fontkit/
├── src/
│   ├── cli.ts                 # CLI implementation
│   ├── optimizer.ts           # Core optimization logic
│   ├── next-plugin.ts         # Next.js plugin
│   ├── index.ts               # Main entry point
│   ├── hooks/
│   │   └── usePersianFont.ts  # React hook
│   └── utils/
│       ├── css.ts             # CSS generation
│       ├── file.ts            # File operations
│       └── subsetConfig.ts    # Subset definitions
├── examples/
│   └── nextjs-demo/           # Next.js example
├── package.json
├── tsconfig.json
└── README.md

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide first.

Development Setup

# Clone repository
git clone https://github.com/kuomars110/persian-fontkit.git
cd persian-fontkit

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run dev

📝 License

MIT © Your Name


🙏 Acknowledgments

  • subset-font - Font subsetting library
  • Commander.js - CLI framework
  • Persian font designers for creating beautiful fonts

📮 Support


Made with ❤️ for the Persian web community

⭐ Star on GitHub📦 npm Package