Essay
←Back to blogZenicons: 3,201 Icons, One npm Package, and Absolutely No Emotional Support for Bad UI Choices
I built a React icon pack with 3,201 typed, tree-shakeable SVGs so your product team can stop screenshotting icons from random dribbble shots at 2 AM. You're welcome.
Zenicons: 3,201 Icons, One npm Package, and Absolutely No Emotional Support for Bad UI Choices
"Another icon library? Bold strategy."
At some point every frontend engineer reaches the same spiritual checkpoint: your app needs one icon, then three, then forty, then suddenly you are debugging SVG stroke alignment at midnight while pretending this is normal adult behavior.
So yes, I made another icon package.
Because clearly what the JavaScript ecosystem was missing was one more icon library named @bhaumic/zenicons with 3,201 icons, strict typing, and a searchable catalogue. Totally unnecessary. Completely avoidable. And yet, deeply useful.
Why I built it (besides poor life decisions)
I wanted icons that were:
- Tree-shakeable by default
- Type-safe in React + TypeScript
currentColorfriendly for real theming- Consistent to generate from raw SVGs
- Publish-safe for npm without shipping accidental chaos
In short: not a random folder of SVGs lovingly copy-pasted across repos for eternity.
What Zenicons gives you
Zenicons ships as a React-first npm package with:
- 3,201 typed icon components
- ESM + CJS + type declarations
sideEffects: falsefor better bundler tree shaking- Shared
IconBasebehavior - A searchable live archive at
mic-360.github.io/zenicons
And yes, it supports practical props:
size(default24)color(defaultcurrentColor)strokeWidth(default1.5)titlefor accessibility labelsvariant(strokeorfill, per icon override)
Because if a component library cannot pass basic accessibility smell tests, it is just decorative debt with a README.
Install like a responsible engineer
npm install @bhaumic/zeniconsnpm install @bhaumic/zeniconsIf you like deterministic installs from source snapshots:
npm install github:Mic-360/zenicons#v0.1.0npm install github:Mic-360/zenicons#v0.1.0Usage (no blood sacrifices required)
import { IconAddressBookEmail, IconSigma } from '@bhaumic/zenicons'
export function Example() {
return (
<div style={{ color: 'tomato' }}>
<IconAddressBookEmail />
<IconAddressBookEmail size={32} color="royalblue" strokeWidth={2} />
<IconSigma size={48} title="Sigma icon" />
</div>
)
}import { IconAddressBookEmail, IconSigma } from '@bhaumic/zenicons'
export function Example() {
return (
<div style={{ color: 'tomato' }}>
<IconAddressBookEmail />
<IconAddressBookEmail size={32} color="royalblue" strokeWidth={2} />
<IconSigma size={48} title="Sigma icon" />
</div>
)
}The icon inherits your text color by default. This is how it should have been from day one, but history had other plans.
The boring engineering bits that actually matter
Most icon packs stop at "it renders." Zenicons also enforces release hygiene:
- Tarball verification before publish
- Controlled publish surface
- npm provenance through GitHub Actions
- Lockfiles committed for package and demo
In other words: fewer supply-chain surprises, fewer accidental file leaks, fewer Friday-night rollback adventures.
Live search, because scrolling 3,201 names manually is a cry for help
The live site exists for one reason: quickly find an icon and copy the import without memorizing naming gymnastics.
- Press
Ctrl/⌘ + Kor/ - Search by fragment
- Copy import instantly
- Move on with your life
Final thoughts
Could I have reused a giant existing icon set and called it a day? Yes.
Would I have learned as much about generation pipelines, packaging discipline, and release safety? Not even close.
Zenicons is for teams that want icons to be infrastructure, not drama.
- npm: @bhaumic/zenicons
- GitHub: Mic-360/zenicons
- Live catalogue: mic-360.github.io/zenicons
Thanks for reading. If this saved you one SVG alignment rage-session, my work here is done.