@web-portfolio/icons
Published and maintain an npm component library (@web-portfolio/icons) — 629 bundled SVG icons behind one typed <Icon /> component, versioned and consumed across multiple projects instead of ad hoc per-icon imports.

It started as the icon set for a personal portfolio site, and grew into something worth publishing on its own: no icon font, no CDN requests at runtime, nothing to load asynchronously. Everything ships inside the package and renders instantly.
Browse the full icon set at icons.getresume.dev — search by name or category, try the color/stroke/size controls on any icon, and copy the exact <Icon /> snippet or the raw SVG straight from the page.
Why this exists
Building a portfolio or dashboard usually means one of two things: pulling in an icon font for a handful of glyphs, or scattering <img src="..."> tags across the codebase pointing at some CDN that may or may not be up when it matters. Neither felt right for something as small as "show the React logo next to the word React." This package is the alternative — <Icon name="react" />, done, and the same component works whether you need a tech-stack badge, a social link icon, or a plain arrow for a carousel button.
A few things it does deliberately
- One component, not 629 imports.
registry[name]does a lookup at render time, so you never end up withimport { ReactIcon, DockerIcon, PostgresIcon } from '...'cluttering the top of a file. - Nothing fetched at runtime. The whole registry is bundled at build time and optimized with SVGO, so there is no loading state, no layout shift while an image loads, and it works offline.
- Themeable where it makes sense.
color,stroke, andstrokeWidthprops recolor monochrome icons to match your palette; brand marks that are supposed to stay a specific color (the React logo's blue, for instance) are left alone on purpose. - Typed out of the box. No separate
@types/package to remember to install.
Installation
npm install @web-portfolio/icons
pnpm add @web-portfolio/icons
yarn add @web-portfolio/icons
Requires React 18 or later.
One registry, two published packages, one static gallery
The published package is one piece of a four-package, two-app monorepo. packages/db holds a Drizzle schema against Turso/libSQL — the only piece that talks to a database, and only ever from apps/admin (a credential-gated CRUD dashboard) and the seed scripts. packages/core is private and internal: it owns the generate-registry build step and the bundled registry.generated.ts both published packages embed. packages/react is @web-portfolio/icons; packages/sanity-plugin is @web-portfolio/icons-sanity, the Studio picker. apps/gallery — the public site at icons.getresume.dev — is the fourth consumer of that same registry, styled after techicons.dev.
apps/gallery reads nothing but the committed registry.generated.ts — no database, no env vars, at request time or build time. An edit made in apps/admin only reaches the public gallery after someone reruns generate-registry and redeploys; the two are deliberately decoupled so neither the live gallery nor the two published packages ever depend on Turso being reachable.
Turning a pasted SVG into a registry entry
generate-registry.ts is what actually builds registry.generated.ts: pull every icon's raw SVG out of Turso, run it through SVGO (multipass, preset-default, removeViewBox held off since the viewBox is split out and kept deliberately), and rewrite it into a compact {viewBox, innerHTML} entry. One rewrite only applies to icons in the monochrome categories — devicon's "plain"/"line" variants, Material Symbols, Simple Icons — swapping any hardcoded fill/stroke for currentColor so the <Icon color> prop actually does something. Brand-color categories (devicon's "original"/"original-wordmark") skip that step entirely, so React's blue or Docker's teal never gets flattened to whatever text color surrounds it.
# one-time: schema + all three icon sources
pnpm --filter @web-portfolio/icons-db db:migrate
pnpm --filter @web-portfolio/icons-db seed-all
# SVGO + currentColor normalization -> packages/core/src/registry.generated.ts
pnpm generate-registryWhere the 633 icons actually come from
Three licensed sources, each attributed and each seeded by its own script: devicon (MIT) supplies 578 tech/tool logos. Material Symbols (Apache-2.0) supplies 38 — 34 everyday UI/navigation glyphs plus 4 generic stand-ins (Bolt, Translate, Checklist, Gift) that replaced Apple SF Symbols PNGs the portfolio site used to ship, which weren't safe to redistribute from an openly licensed package. Simple Icons (CC0) supplies the remaining 17 — 12 social-platform logos devicon doesn't cover, and 5 developer-tool marks (MCP, Google Analytics, LangChain, Ollama, Pydantic).
Not every requested icon has a licensed source to pull from — Segment, Statsig, and Google Stitch aren't in any of the three (Stitch postdates all of their latest releases). Rather than scrape a brand asset with no clear redistribution license, those go through apps/admin's "paste raw SVG" flow by hand instead, on the person adding it, not the pipeline.
Every icon in the registry can point at exactly which source it came from and what license that source carries — worth more than one more logo would have been.

