v0.4.1 on npm · MIT · Node ^22.20 || >=24.11

Your SVGs.
One sprite.

Drop in the SVGs your designers export. svgworm writes one content-hashed sprite and a type with every icon name. Logos keep their colors, mono icons follow currentColor, and a typo fails tsc. Render it in Next.js Server Components, Vite or Astro with zero client JS, and no Turbopack plugin.

Install command for

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

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

pnpm add -D svgworm

Only using lucide or another icon set? Its React package is fine. svgworm is for the icons you own.

Or paste this into your coding agent

Set up svgworm in this project for my SVG icons. Read https://svgworm.com/llms.txt, detect the framework (Next.js, Vite + React or Astro), follow the matching guide linked there, and run the build to check it works.
~/my-app
$ pnpm svgworm
svgworm: build done
  icons     23 (17 mono, 6 multicolor)
  delivery  use 23, img 0, mask 0
  output    11.5 KiB
  write     public/icons/icons.ce28be92.svg

$ pnpm tsc
error TS2322: Type '"termnial"' is not assignable to type 'IconName'.
0 BJavaScript for icons
1 requestfor all 23 icons
11.5 KiBthe whole sprite
1 yearimmutable browser cache

These numbers are from this page's own build: the sprite it loads and the headers it is served with.

From folder to typed icon

What svgworm does to your SVG files.

A curated demo, not a live build. The file names come from this site's own icons/ folder. The icons in step 3 are drawn from this page's real sprite.

  1. Your SVG files

    One file per icon, in any folders you like.

    icons/
    ├── check.svg
    ├── folder-open.svg
    ├── terminal.svg
    └── emoji/
        └── snail.svg
  2. One sprite and a type

    svgworm writes a hashed sprite and the icon names as a type. A wrong name fails tsc.

    // generated
    public/icons/icons.ce28be92.svg
    svgworm/icons.d.ts
    
    // svgworm/icons.d.ts
    type IconName =
      | "arrow-right"
      | "book-open"
      | "check"
      | …
    <Icon name="terminal" />
    <Icon name="termnial" />
    // error TS2322: "termnial" is
    // not assignable to IconName
  3. Rendered from the sprite

    Each icon is a <use href> into/icons/icons.ce28be92.svg. No icon code reaches the browser.

    checkfolder-openterminalemoji/snail

Why svgworm

Icons that render right, with no config.

A sprite tool has to decide, per icon, whether to turn its fills intocurrentColor. Do it for the whole folder and logos turn into black blobs; never do it and mono icons ignore dark mode. svgworm decides per icon: on 31,390 icons it agreed with the rendering 99.98% of the time, and it recolored 0 multicolor icons. How it decides.

Everything else stays out of your bundle: SVGR turns every icon into JavaScript, svgworm ships one cached file and a name. How it compares with SVGR and @neodx/svg.

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><use href="/icons/icons.ce28be92.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. They are byte-identical on Linux, macOS and Windows. Turbopack, webpack and Vite need no plugin. svgworm check fails CI when the output is stale.

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 skills install svgworm in a Next.js or an Astro app. They also explain each diagnostic code with its fix.

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

Moving from SVGR, or mixing an icon set with your own SVGs?

Move them into one sprite. Paste one of these prompts into your agent.

From SVGR

Migrate this project's SVG icons from SVGR to svgworm. Read https://svgworm.com/llms.txt first and set svgworm up with the guide for this project's framework, with the folder that holds the SVG files SVGR imports as the svgworm source. Replace every SVGR icon import and its component with the svgworm icon component, keeping the same icons. Remove the SVGR loader rules from the bundler config and @svgr/webpack from package.json once nothing uses them. Run the build and the type check to confirm every icon name resolves.

From lucide-react

Migrate this project from lucide-react to svgworm. Read https://svgworm.com/llms.txt first and set svgworm up with the guide for this project's framework. Then copy the SVG source of each lucide-react icon the code uses into the icons folder, replace every lucide-react import with the svgworm icon component, and remove lucide-react from package.json once nothing imports it. Run the build and the type check to confirm every icon name still resolves.

From react-icons

Migrate this project from react-icons to svgworm. Read https://svgworm.com/llms.txt first and set svgworm up with the guide for this project's framework. Then copy the SVG source of each react-icons icon the code uses into the icons folder, replace every react-icons import with the svgworm icon component, and remove react-icons from package.json once nothing imports it. Run the build and the type check to confirm every icon name still resolves.

This page is the demo

Every icon here is one sprite.

/icons/icons.ce28be92.svg, rendered by the generated Icon.astro at build time. It is served with Cache-Control: immutable. Open your network panel: one icon sprite and no JavaScript to render it.

Multicolor 6 icons, colors kept

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

Mono 17 icons, follow currentColor

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

Same sprite, same <use href>, decided per icon. Pick a text color. The mono folder follows it; the multicolor snail keeps its own colors. No JavaScript, only CSS color.

Text color

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. Then it builds once, so the app runs before you add an icon.

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

    Each run writes a hashed sprite to public/icons/ and a typed manifest to svgworm/. predev and prebuild run it for you.

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

    Decorative by default. Pass title orlabel for an accessible name.

    <Icon name="home" />
    <Icon name="home" title="Home" />

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

Pick your framework

One guide per stack.

Astro

A dependency-free Icon.astro written by init.

Point it at your icon folder.

One init, Next.js, Vite or Astro, and your icons ship as one cached file.

Install command for

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

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

pnpm add -D svgworm