npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-icons-sprite

v0.6.1

Published

`react-icons-sprite` is a lightweight plugin for Vite and Webpack that turns React icon components into a single SVG spritesheet and rewrites your code to reference those symbols via `<use>`.

Downloads

370

Readme

react-icons-sprite

react-icons-sprite is a lightweight plugin for Vite and Webpack that turns React icon components into a single SVG spritesheet and rewrites your code to reference those symbols via <use>.

It supports multiple React icon packages that export icons as individual React components. This approach both shrinks your bundle (no more inlined React components for every icon) and reduces runtime overhead, since React no longer has to reconcile large, nested SVG trees.

Supported icon libraries

Out of the box, imports from the following libraries are detected and transformed:

  • react-icons/* packs (e.g. react-icons/bi, react-icons/fa, ...)
  • lucide-react
  • @radix-ui/react-icons
  • @heroicons/react (v1 and v2 subpaths)
  • @tabler/icons-react
  • phosphor-react
  • @phosphor-icons/react
  • react-feather
  • react-bootstrap-icons
  • grommet-icons
  • remixicon-react
  • @remixicon/react
  • devicons-react

Motivation

By default, when you use an icon library like react-icons, each icon is a React component. For example:

import { LuWheat } from "react-icons/lu";

export function Example() {
  return <LuWheat />;
}

looks harmless, but at build time this compiles to something like:

import { b as e } from './iconBase-BU3rGdXB.js'

function t(t) {
  return e({
    tag: `svg`, attr: { viewBox: `0 0 640 512` }, child: [{
      tag: `path`, attr: {
        d: `M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z`
      }, child: []
    }]
  })(t)
}

function n(t) {
  return e({
    tag: `svg`, attr: { viewBox: `0 0 496 512` }, child: [{
      tag: `path`, attr: {
        d: `M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z`
      }, child: []
    }]
  })(t)
}

// ... potentially dozens more child components.

export { t, n };

That means:

  • Every icon adds hundreds of characters of JSX to your bundle.
  • At runtime, React must create and diff dozens of DOM nodes for every icon render.
  • If you render 100 icons, React is reconciling potentially thousands of elements.

Now compare that to output when using a spritesheet with <use>:

import { LuWheat } from "react-icons/lu";

export function Example() {
  return <LuWheat />;
}
import { b as n } from './icon-FONPSuqX.js';

var r = e(t());
const i = () => (0, r.jsx)(n, { iconId: `ri-react-icons-lu-LuWheat` });
export { i as IconWheat };

icon-FONPSuqX.js in this case is our simple icon wrapper, which looks like this:

var n = e(t());
const r = ({ iconId: e, ...t }) => {
  let r = `assets/react-icons-sprite-C-JClopV.svg#${e}`;
  return (0, n.jsxs)(`svg`, {
    height: `1em`,
    width: `1em`,
    preserveAspectRatio: `xMidYMid meet`,
    viewBox: `0 0 24 24`, ...t,
    children: [(0, n.jsx)(`title`, { children: e }), (0, n.jsx)(`use`, { href: r })]
  })
};
export { r as b };

Runtime difference:

  • React only reconciles two DOM nodes (<svg> + <use>).
  • All heavy path data lives in the static spritesheet once, not duplicated across components.
  • Updating 100 icons is as cheap as updating 100 <use> tags.

This is a big win when you’re rendering icons in lists, tables, or maps where dozens or hundreds of them appear at once.

Performance comparison

| icon (pack) | react-icons icon render mean time | react-icons-sprite icon render mean time | Relative difference | |--------------------|----------------------------------:|---------------------------------------------:|------------------------:| | FiCpu (fi) | 0.188 ms | 0.048 ms | 74.6% reduction | | MdBuild (md) | 0.198 ms | 0.048 ms | 76.0% reduction | | FaCamera (fa) | 0.162 ms | 0.015 ms | 90.7% reduction | | IoAperture (io5)| 0.029 ms | 0.015 ms | 49.7% reduction | | BiBell (bi) | 0.023 ms | 0.014 ms | 38.5% reduction | | AiOutlineAlert (ai) | 0.023 ms | 0.014 ms | 38.3% reduction | | BsAlarm (bs) | 0.027 ms | 0.014 ms | 47.4% reduction | | RiAnchor (ri) | 0.023 ms | 0.014 ms | 38.6% reduction | | CgArrows (cg) | 0.029 ms | 0.014 ms | 52.0% reduction | | HiAcademicCap (hi) | 0.023 ms | 0.014 ms | 38.6% reduction | | SiTypescript (si) | 0.023 ms | 0.014 ms | 39.7% reduction | | TiThLarge (ti) | 0.023 ms | 0.014 ms | 40.0% reduction |

  • Test details / machine: Lenovo Legion 5 Pro 16ACH6H (Ryzen 7 5800H — 8 cores / 16 threads, base ≈ 3.2 GHz, turbo ≈ 4.4 GHz, DDR4-3200 memory); Node.js v24.10.0.
  • Differences will vary based on icons used in your application, but they will generally be the range of 50-75% reduction in render time. Larger icons will generate a larger difference.

Installation

Install the plugin via npm or yarn:

npm install --save-dev react-icons-sprite

Vite

Add the plugin to the plugins array in your Vite config.

// vite.config.ts
import { defineConfig } from 'vite';
import { reactIconsSprite } from 'react-icons-sprite/vite';

export default defineConfig({
  plugins: [reactIconsSprite()],
});

Webpack

Add the loader to transform modules that import icons and install the plugin to emit the sprite and rewrite the placeholder URL.

// webpack.config.js (v5)
const path = require('path');
const { reactIconsSprite } = require('react-icons-sprite/webpack');

module.exports = {
  mode: 'production',
  // ... your existing config
  module: {
    rules: [
      {
        test: /\.(mjs|cjs|js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: require.resolve('react-icons-sprite/webpack/loader'),
          },
          // put your ts/tsx loader after ours (e.g. babel-loader or ts-loader)
        ],
      },
    ],
  },
  plugins: [
    reactIconsSprite({
      // optional: fileName: 'icons.svg'
    }),
  ],
};

How it works

In development mode, the plugin does nothing special. Icons are rendered as they normally would from your icon library. This keeps hot module replacement (HMR) snappy — there’s no extra parsing of the codebase or regenerating of the sprite on every save. If the plugin were to build the sprite during dev, it would need to constantly scan for icon imports and rebuild the sheet, which is expensive and slows down iteration. So, in dev, you get the normal component behavior.

In build mode, the plugin transforms your code. It parses each module, looks for imports from supported React icon packages, and rewrites the JSX. Instead of rendering full inline <svg> trees, it replaces them with <ReactIconsSpriteIcon iconId="..." />. While doing this, it collects every unique icon used across the project. After the bundling step, the plugin renders all those icons once to static markup and generates a single SVG file containing <symbol> definitions for each one. Finally, it rewrites your bundle to point every <ReactIconsSpriteIcon> at that spritesheet using a <use> tag.

The result: during development you keep fast feedback loops, and in production you ship a single optimized sprite file with lightweight <use> references.

Contributing

Contributions are welcome! Feel free to open an issue or submit a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for details.