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 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-svg-symbols

v1.2.0

Published

Vite plugin for creating SVG symbol sprite

Readme

vite-plugin-svg-symbols

GitHub license

Vite plugin for creating SVG symbol sprite 🧞‍♂️

Features

  • Create a single sprite from SVG files
  • Use a sprite as a static file for JS bundle optimization
  • The production build includes only the icons that are actually used
  • Support mass import or by one
  • Compatible with modern frameworks: Vue / Svelte / React / Solid / Preact

Navigation

Getting Started

1. Install using npm:

npm i vite-plugin-svg-symbols -D

2. Add to your vite.config.ts:

import { defineConfig } from 'vite';
import { fileURLToPath, URL } from 'node:url';
import useSvgSymbols from 'vite-plugin-svg-symbols'; // ← [1] import plugin

export default defineConfig({
  plugins: [
    useSvgSymbols(), // ← [2] use plugin
  ],
  resolve: {
    alias: {
      '@icons': fileURLToPath(new URL('./src/icons', import.meta.url)), // ← [3] add alias
    },
  },
});

3. Add to your vite-env.d.ts or env.d.ts:

/// <reference types="vite-plugin-svg-symbols/client" />

[!TIP] If your project doesn't include an env.d.ts file, you can specify the type reference in tsconfig.app.json:

"compilerOptions": {
  "types": ["vite-plugin-svg-symbols/client"]
}

4. Finally, add the vpss command before type checking in package.json:

[!IMPORTANT] The vpss command generates types for icons to avoid errors such as:

error TS2614: Module 'svg:symbols@icons' has no exported member ...

"scripts": {
  "build": "vpss && tsc -b && vite build"
}

Another option is to run type checking after the build process; since types are generated during the build, no extra command is required:

"scripts": {
  "build": "vite build && tsc -b"
}

Usage

For example, you have src/icons/cat.svg and src/icons/dog.svg and @icons alias in vite.config.ts

Plain import

import dogIcon from '@icons/dog.svg?symbol'; // import icon as symbol href

Mass import

import { dogIcon, catIcon } from 'svg:symbols@icons'; // import two icons as symbol hrefs

Icon packages

You can add alias for library, for example @lucide (lucide):

// vite.config.ts
alias: {
  '@lucide': fileURLToPath(new URL('./node_modules/lucide-static/icons/', import.meta.url)),
},

Then import icons from lucide package:

import bananaIcon from '@lucide/banana.svg?symbol';
import { appleIcon, bananaIcon } from 'svg:symbols@lucide';

Import names

By default, icon names are defined in camel case with the Icon suffix:

banana.svg → bananaIcon
a-arrow-up.svg → aArrowUpIcon
arrow-down-a-z.svg → arrowDownAZIcon
arrow-down-1-0.svg → arrowDown10Icon

// subdir
home/foo.svg → homeFooIcon

// when same names
arrow-down-0-1.svg → arrowDown01Icon
arrow-down-01.svg  → arrowDown01Icon2 (because arrowDown01Icon already exist)

Examples

When you have src/icons/cat.svg and src/icons/dog.svg and @icons alias in vite.config.ts

Vue / Svelte / React / Solid / Preact

Vue

Plain import:

<script setup lang="ts">
import dogIcon from '@icons/dog.svg?symbol';
</script>

<template>
  <svg aria-hidden="true">
    <use :href="dogIcon" /> // use dogIcon from script
  </svg>

  <svg aria-hidden="true">
    <use href="@icons/cat.svg?symbol" /> // or import icon directly
  </svg>

  <!-- Don't do this -->
  <svg aria-hidden="true">
    <use :href="'@icons/cat.svg?symbol'" /> // ⚠️ dynamic path not working
  </svg>
  <!-- :( -->

</template>

Mass import:

<script setup lang="ts">
import { dogIcon, catIcon } from 'svg:symbols@icons'; // import two icons
</script>

<template>
  <svg aria-hidden="true">
    <use :href="dogIcon" />
  </svg>

  <svg aria-hidden="true">
    <use :href="catIcon" />
  </svg>
</template>

You can create a very simple component BaseIcon.vue:

<script setup lang="ts">
defineProps<{ src: string }>()
</script>

<template>
  <svg aria-hidden="true">
    <use :href="src" />
  </svg>
</template>
<script setup lang="ts">
import { dogIcon, catIcon } from 'svg:symbols@icons';
import BaseIcon from '@/components/BaseIcon.vue';
</script>

<template>
  <BaseIcon :src="dogIcon" />
  <BaseIcon :src="catIcon" />
</template>

Svelte

Plain import:

<script lang="ts">
import dogIcon from '@icons/dog.svg?symbol';
</script>

<main>
  <svg aria-hidden="true">
    <use href={dogIcon} />
  </svg>
</main>

Mass import:

<script lang="ts">
import { dogIcon, catIcon } from 'svg:symbols@icons'; // import two icons
</script>

<main>
  <svg aria-hidden="true">
    <use href={dogIcon} />
  </svg>

  <svg aria-hidden="true">
    <use href={catIcon} />
  </svg>
</main>

You can create a very simple component BaseIcon.svelte:

<script lang="ts">
  export let src = '';
</script>

<svg aria-hidden="true">
  <use href={src} />
</svg>
<script lang="ts">
import { dogIcon, catIcon } from 'svg:symbols@icons';
import BaseIcon from './BaseIcon.svelte';
</script>

<main>
  <BaseIcon src={dogIcon} />
  <BaseIcon src={catIcon} />
</main>

React & Solid & Preact

Plain import:

import dogIcon from '@icons/dog.svg?symbol'

function App() {
  return (
    <>
      <svg aria-hidden="true">
        <use href={dogIcon} />
      </svg>
    </>
  )
}

export default App

Mass import:

import { dogIcon, catIcon } from 'svg:symbols@icons' // import two icons

function App() {
  return (
    <>
      <svg aria-hidden="true">
        <use href={dogIcon} />
      </svg>

      <svg aria-hidden="true">
        <use href={catIcon} />
      </svg>      
    </>
  )
}

export default App

You can create a very simple component BaseIcon.tsx:

function BaseIcon({ src = '' }) {
  return (
    <svg aria-hidden="true">
      <use href={src} />
    </svg>
  )
}
export default BaseIcon
import { dogIcon, catIcon } from 'svg:symbols@icons' // import two icons
import BaseIcon from './BaseIcon'

function App() {
  return (
    <>      
      <BaseIcon src={dogIcon} />
      <BaseIcon src={catIcon} />
    </>
  )
}

export default App

Options

type SymbolPayload = {
  symbolId: string
  symbolBody: string
  symbolViewBox: string
  svgBody: string
  svgPath: string
  svgAttrs: Record<string, string>
}

type SvgSymbolsPluginOptions = {
  fileName?: string
  shouldInjectToHtml?: boolean
  injectAttrs?: Record<string, string | boolean | undefined>
  transformIcon?: (iconCode: string, iconPath: string) => string | Promise<string>
  transformIconId?: (iconId: string, iconPath: string) => string | Promise<string>
  transformSprite?: (body: string) => string | Promise<string>
  transformSymbol?: (symbolCode: string, payload: SymbolPayload) => string | Promise<string>
  transformImportName?: (name: string, iconPath: string) => string | Promise<string>
  transformImportComment?: (comment: string, iconPath: string) => string | Promise<string>
}

fileName

{
  fileName: 'my-sprite-name-[hash]', // default 'sprite-[hash]'
}

shouldInheritAttrs

{
  shouldInheritAttrs: [], // List of SVG attributes to inherit in <symbol>
}
// default: [
//   'fill',
//   'stroke',
//   'stroke-dasharray',
//   'stroke-dashoffset',
//   'stroke-linecap',
//   'stroke-linejoin',
//   'stroke-miterlimit',
//   'stroke-opacity',
//   'stroke-width',
// ];
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
  <circle cx="8" cy="8" r="8"></circle>
</svg>

<!-- Become -->
<symbol viewBox="0 0 16 16" id="some-id">
  <g fill="none" stroke="currentColor" stroke-width="2">
    <circle cx="8" cy="8" r="8"></circle>
  </g>
</symbol>

shouldInjectToHtml

{
  shouldInjectToHtml: true, // When set to true, injects the sprite into index.html
}

injectAttrs

{
  injectAttrs: { ... }, // Defines sprite attributes when injected into index.html
}
// default:
// {
//  'aria-hidden': 'true',
//   style: 'position:absolute;top:0;left:0;width:1px;height:1px;visibility:hidden;opacity:0;',
// }

transformIcon

// Callback function to transform an icon body, for example, to optimize it with svgo:

const { optimize } = require('svgo');
const svgoOptions = {
  plugins: [
    {
      name: 'preset-default',
      params: {
        overrides: {
          removeComments: true,
        },
      },
    },
  ],
};
{
  transformIcon: (iconCode, iconPath) => {
    const optimizationResult = optimize(iconCode, svgoOptions);
    return optimizationResult.data;
  },
}

transformIconId

// Callback function to transform an icon id, for example, when you have:
// src/icons/cat.svg
{
  transformIconId: (iconId, iconPath) => {
    console.log(iconId); // 'icons-cat'
    console.log(iconPath); // '/Users/werty1001/Desktop/app/src/icons/cat.svg'
    return iconId;
  },
}

transformSprite

// Callback function to transform the sprite body, for example, to prepend <defs>:
{
  transformSprite: (body) => {
    return '<defs>...</defs>'.concat(body);
  },
}

transformSymbol

// Callback function to transform the sprite symbol, for example, when you have:
// src/icons/cat.svg
{
  transformSymbol: (symbolCode, payload) => {
    console.log(symbolCode); // '<symbol ...'
    console.log(payload);
    // {
    //   symbolId: 'icons-cat',
    //   symbolBody: '<path ...',
    //   symbolViewBox: '0 0 24 24',
    //   svgBody: '<path ...',
    //   svgPath: '/Users/werty1001/Desktop/app/src/icons/cat.svg',
    //   svgAttrs: { viewBox: '0 0 24 24', ... }
    // }
    return symbolCode;
  },
}

transformImportName

// Callback function to transform import name, for example, when you have:
// src/icons/cat.svg
{
  transformImportName: (name, iconPath) => {
    console.log(name); // 'catIcon'
    console.log(iconPath); // '/Users/werty1001/Desktop/app/src/icons/cat.svg'
    return name;
  },
}
// import { catIcon } from 'svg:symbols@icons';

transformImportComment

// Callback function to transform import comment,
// for example, when you have lucide package:

import { basename } from 'path';

const lucideComment = `/**
 * Lucide
 * @see https://lucide.dev/icons/[name]
 */`;
{
  // Replace the default comment to enable IDEs to show
  // tooltips with a link to the icon at https://lucide.dev

  transformImportComment: (comment, iconPath) => {
    if (iconPath.includes('lucide-static')) {
      return lucideComment.replace('[name]', basename(iconPath, '.svg'));
    }
    return comment;
  },
}