v0.1 on npm · MIT · Node 22.20+

One sprite.
Zero client JS.

svgworm digs through your folder of SVG files and leaves one type-safe, content-hashed sprite behind. Render it from React Server Components in the Next.js App Router, and ship no icon code to the browser.

pnpm add -D svgworm && pnpm add @svgworm/react

~/my-app
$ pnpm svgworm
svgworm: build done
  icons     19 (13 mono, 6 multicolor)
  delivery  use 19, img 0, mask 0
  output    10.2 KiB
  write     public/icons/icons.7ced95d4.svg

$ pnpm tsc
error TS2322: Type '"termnial"' is not assignable to type 'IconName'.
0 BJavaScript on this page
1request for all 19 icons
10.2 KiBthe whole sprite
1 yearimmutable browser cache

Why svgworm

Icons that stay out of your bundle.

Rendered on the server

Each icon is a tiny <use href> into one hashed sprite. The browser fetches the sprite once and keeps it.

<Icon name="terminal" />
// <svg aria-hidden="true"><use href="/icons/icons.7ced95d4.svg#i-terminal"/></svg>

Type-safe names

The generated types list every icon. A typo fails tsc, not production.

<Icon name="termnial" />
  • Autocomplete in your editor
  • Checked with TypeScript 6 and 7
  • Clear errors for collisions

Right colors, per icon

Mono icons follow currentColor, multicolor icons keep their colors. Gradients, masks and filters render in Chromium, Firefox and WebKit.

Plain files, no plugin

A CLI that writes static files, byte-identical on Linux, macOS and Windows. Turbopack, webpack and Vite need no plugin, and svgworm check fails CI when the output is stale.

Get started in Next.js

Three steps, no bundler config.

  1. Install and set up

    init writes the config, the predev/prebuild scripts and the component.

    pnpm add -D svgworm
    pnpm add @svgworm/react
    pnpm svgworm init
  2. Drop SVGs in icons/

    Each run writes a hashed sprite to public/icons/ and a typed manifest to svgworm/.

    icons/
    ├── home.svg
    ├── search.svg
    └── brand/logo.svg
  3. Render in a Server Component

    Decorative by default. Pass title or label for an accessible name.

    import { Icon } from '@/components/icon';
    
    <Icon name="home" title="Go home" />

Caching, basePath and manual setup are in the Next.js guide. A complete app lives in examples/next-app-router.

This page is the demo

Every icon here is one sprite.

/icons/icons.7ced95d4.svg, rendered by the generated Icon.astro at build time and served with Cache-Control: immutable. Open your network panel: no JavaScript, one SVG.

Multicolor 6 icons, colors kept

  • emoji/carrot
  • emoji/mushroom
  • emoji/rock
  • emoji/seedling
  • emoji/snail
  • emoji/worm

Mono 13 icons, follow currentColor

  • arrow-right
  • book-open
  • check
  • file-code
  • folder-open
  • gauge
  • git-branch
  • layers
  • package
  • server
  • shield-check
  • terminal
  • zap

Same sprite, same <use href>, decided per icon. Change the text color: the mono folder follows it, the multicolor snail keeps its own colors.

Built for AI agents too

Your agent can set it up.

Everything works without a prompt: flags, --json, stable exit codes, a JSON Schema for the config, and llms.txt. The agent skill installs svgworm in a Next.js app and fixes every error code.

$ npx skills add jmlweb/svgworm --skill svgworm-nextjs

Point it at your icon folder.

Two packages, one init, and your icons ship as one cached file.

pnpm add -D svgworm && pnpm add @svgworm/react