<Icon /> in development. Ship one SVG sprite in production - only the icons you used.
|
π Zero Runtime Compiles to native <use> tags
|
π¦ ~300% Smaller No HTML bloat or JS overhead |
π¨ 6,800+ Icons Full Lucide & Tabler support |
|
π οΈ DX First Full component DX in Dev |
β‘ Any Framework Next.js, Vite, Remix, Webpack |
π§© Custom Icons First-class support for your SVGs |
SVG sprites are still the most performant way to deliver flat icons on the web. They are significantly smaller than React components and can be cached aggressively by the browser.
However, using sprites in development is a pain. Browsers cache sprites so aggressively that you often have to close the tab and reopen it just to see a new icon, even with cache disabled in DevTools.
This library solves that problem:
- In Development: It uses standard React components. No caching issues, instant HMR (Hot Module Replacement), and full prop support.
- In Production: It compiles everything into a single, highly optimized SVG sprite that is loaded once and cached forever.
You get the Developer Experience of a component library with the Performance of a handwritten sprite.
Note
See the difference for yourself: View Live Demo β
| Library | HTML Size |
|---|---|
| Lucide React | 19.5kb |
| @react-zero-ui | 7.5kb |
npm install @react-zero-ui/icon-spriteimport { ArrowRight, Mail } from "@react-zero-ui/icon-sprite";
<ArrowRight size={24} className="text-gray-600" />
<Mail width={24} height={24} />Caution
Run this before your app build so the sprite exists.
npx zero-iconsOr add it to your package.json:
{
"scripts": {
"prebuild": "zero-icons",
"build": "your build command"
}
}That's it! Your icons are now optimized for production.
import { ArrowRight, Mail } from "@react-zero-ui/icon-sprite";
<ArrowRight size={24} className="text-gray-600" />
<Mail width={24} height={24} />import { IconBrandGithub, IconHeart } from "@react-zero-ui/icon-sprite";
<IconBrandGithub size={24} className="text-gray-600" />
<IconHeart width={24} height={24} />Drop your own SVGs into /public/zero-ui-icons/, then use <CustomIcon />:
Tip
π/public
βββπ/zero-ui-icons/
βββdog.svgimport { CustomIcon } from "@react-zero-ui/icon-sprite";
<CustomIcon name="dog" size={24} />The name prop must match the file name (without .svg).
Note
In dev you may see a brief FOUC using custom icons; this is removed in production.
π Click to see how we handle Dev vs. Prod
In dev, each icon wrapper looks like this:
import { ArrowRight as DevIcon } from "lucide-react";
export const ArrowRight = (props) =>
process.env.NODE_ENV === "development" ? (
<DevIcon {...props} />
) : (
<svg {...props}>
<use href={`/icons.svg#arrow-right`} />
</svg>
);This ensures:
- Dev uses real React components
- Full props support (e.g.
strokeWidth,className) - No caching issues from SVG sprites
- No FOUC (Flash of Unstyled Content)
At build time:
- We scan your codebase for all icons statically using Babel + AST traversal
- We generate a single SVG sprite sheet (
public/icons.svg) - The wrapper components switch to
<use href="/icons.svg#icon-id" />
| Script | Purpose |
|---|---|
scan-icons.js |
Parse your codebase for used icons (Icon usage or named imports) |
used-icons.js |
Collects a list of unique icon names |
build-sprite.js |
Uses svgstore to generate icons.svg from used Lucide + Tabler + custom SVGs |
Part of the React Zero-UI ecosystem.
Made with β€οΈ for the React community by @austinserb