Vue and Nuxt setup

Set svgworm up in a Vue or Nuxt app in three steps. The Icon component is a plain .vue file that init writes into your project. It needs no extra package, and it renders the same markup as the React Icon. With server rendering, icons show before any JavaScript runs.

Requires Node ^22.20 || >=24.11 and Vue 3.5 (Nuxt 4 runs on it).

Steps#

  1. Install and set up:

    pnpm add -D svgworm && pnpm svgworm init
    # npm:  npm install -D svgworm && npx svgworm init
    # yarn: yarn add -D svgworm && yarn svgworm init

    init sees the vue or nuxt dependency and does the setup for you:

    • Files: writes svgworm.config.json, the predev/prebuild scripts and the component. Then it builds once.
    • Where the component goes:
      • Nuxt 4: app/components/Icon.vue.
      • Nuxt 3, or Nuxt 4 that keeps the Nuxt 3 layout (no app/ folder, or app.vue at the root): components/Icon.vue.
      • With @nuxt/icon or Nuxt UI, the file is SvgIcon.vue instead, rendered as <SvgIcon>, because those modules already register <Icon>.
      • Otherwise: src/components/Icon.vue.
    • Sample icon: if icons/ is missing or empty, it adds icons/svgworm.svg so the first build works.
    • Flags: with --yes it asks nothing. Use --framework vue when the project has no vue dependency yet.

    See the CLI reference for every flag.

  2. Put your SVG files in icons/ (delete the sample) and build:

    pnpm svgworm

    This writes the sprite to public/icons/ and the manifest and types to svgworm/. Vite and Nuxt serve public/ as is. While you work, run svgworm watch next to the dev server to rebuild on each change.

  3. Use it in any component:

    <script setup lang="ts">
    // Nuxt auto-imports it: skip this line there.
    import Icon from "../components/Icon.vue";
    </script>
    
    <template>
      <nav>
        <Icon name="home" />
        <Icon name="brand/sunset" :width="32" :height="32" />
        <Icon name="home" title="Go home" class="nav-icon" />
      </nav>
    </template>
    • Names come from the file paths: home is icons/home.svg and brand/sunset is icons/brand/sunset.svg, lower-cased with - between words.
    • Type-checked: name is typed from the generated IconName, so a typo fails vue-tsc (and nuxi typecheck).
    • Decorative by default: the icon is aria-hidden unless you pass title or label.
    • Attributes pass through: class, style (a string, an object or an array, as in any Vue template), id, data-* and aria-* land on the rendered element.

@nuxt/icon (and Nuxt UI, which uses it) registers a global <Icon>. When either is a dependency, init names the file SvgIcon.vue, which Nuxt auto-imports as <SvgIcon>. If you add the module later, rename one of the two: its icon.componentName option (for example NuxtIcon), or your file.

Icon.vue is your file, and init never overwrites it. To refresh it after upgrading svgworm, delete it and run svgworm init again.

Set up by hand#

Without init, run the CLI before dev and build yourself:

{ "scripts": { "predev": "svgworm", "prebuild": "svgworm" } }

Yarn 2+ never runs pre scripts. With it, put the command in front of the scripts instead, for example "dev": "svgworm && nuxt dev". Then copy the component from svgworm init --framework vue in a scratch folder.

With Vite, the optional svgworm/vite plugin can rebuild icons inside vite dev. It does not depend on React. See the plugin section of the Vite setup.

base#

Sprites on a CDN#

init --cdn-url is not supported for Vue yet: crossOrigin "inject" needs a loader <script> in the page, and init does not write one for Vue (ADR-0034). Icon.vue already renders the in-page references that mode needs. Until it is, serve the sprites from your own origin.

Cache for a year#

The file names are content-hashed, so a long cache is safe. Send Cache-Control: public, max-age=31536000, immutable for /icons/* from your host, as in the Astro setup.

What to commit and CI#

Commit public/icons/ and svgworm/ and run svgworm check in CI, before the build. It fails when someone changed an icon and forgot to rebuild. Or ignore both folders and rely on the predev and prebuild scripts: then anything that type-checks before the build must run svgworm first.

pnpm svgworm check
pnpm build

How it is checked#

A conformance test compiles the generated Icon.vue for the server and checks that it renders the same markup as the React Icon from the same manifest, for every delivery. vue-tsc checks that an unknown name is a type error.

Next steps#

Edit this page on GitHub