nuxt-svg-icon-module
v1.5.1
Published
Nuxt module for lazy-loading inline SVG icons from your assets directory
Downloads
1,255
Maintainers
Readme
nuxt-svg-icon-module
Nuxt module for lazily loading inline SVG icons straight from your assets/ directory. Icons inherit currentColor automatically so they fit naturally into any design system.
Features
- Lazy-loaded — each SVG is loaded on demand via
import.meta.glob, no upfront bundle cost - Inline SVG — full CSS control:
color,stroke,fill,width,height currentColorby default — icons follow the text color of their container- Subdirectory support — organise icons into folders:
<Icon name="social/github" /> - Reactive — changing the
nameprop swaps the icon without a page reload - SSR-safe — uses
useAsyncDatawith a unique key per instance, no hydration mismatches - Configurable — custom icons directory, component name, and CSS prefix
- Automatic optimization — SVGs are optimized via SVGO at build time (configurable or opt-out)
Compatibility
| nuxt-svg-icon-module | Nuxt | Node.js |
|---------------|-------|---------|
| ^1.0.0 | ^3.10 \|\| ^4.0 | >=18 |
Setup
# pnpm
pnpm add nuxt-svg-icon-module
# npm
npm install nuxt-svg-icon-module
# yarn
yarn add nuxt-svg-icon-module// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-svg-icon-module'],
})That's it. The <Icon> component is now available everywhere in your app — no imports needed.
Usage
Place your SVG files in assets/icons/:
assets/
icons/
check.svg
close.svg
social/
github.svg
twitter.svgUse the component anywhere:
<!-- Basic usage -->
<Icon name="check" />
<!-- Preserves original SVG colors -->
<Icon name="check" filled />
<!-- Subdirectory -->
<Icon name="social/github" />
<!-- Dynamic name -->
<Icon :name="currentIcon" />Icons inherit color from their parent:
<p style="color: tomato;">
<Icon name="check" /> Done
</p>By default icons are 1em × 1em. To set a fixed size, apply width and height directly on the component or wrap it in a container:
<!-- square icon -->
<Icon name="check" style="width: 24px; height: 24px" />
<!-- non-square icon -->
<Icon name="logo" style="width: 124px; height: 40px" />
<!-- via a wrapper -->
<div style="width: 124px; height: 40px">
<Icon name="logo" />
</div>Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-svg-icon-module'],
svgIcon: {
// Directory containing SVG files, relative to project root
iconsDir: 'assets/icons', // default
// Name of the globally registered component
componentName: 'Icon', // default
// CSS class prefix applied to the wrapper <span>
prefix: 'icon', // default
// Automatically optimize SVGs via SVGO at build time
optimize: true, // default
// Custom SVGO configuration (see https://svgo.dev/docs/configuration/)
svgoConfig: {}, // default
},
})Props
| Prop | Type | Default | Description |
|----------|-----------|---------|----------------------------------------------------------------|
| name | string | — | Icon path relative to iconsDir, without .svg extension |
| filled | boolean | false | When true, preserves the SVG's original fill and stroke colors |
| label | string | — | Accessible label announced by screen readers. When omitted, the icon is hidden from assistive technologies (aria-hidden="true") |
Accessibility
By default, icons are decorative and hidden from screen readers via aria-hidden="true". When an icon carries meaning, pass the label prop — the icon will be announced by assistive technologies:
<!-- decorative — screen readers skip this -->
<Icon name="check" />
<!-- meaningful — screen readers announce "Close dialog" -->
<Icon name="close" label="Close dialog" />TypeScript: IconName type
The module generates an IconName type — a string union of every icon name derived from your iconsDir at build time. It is automatically available in vue component props via the global components augmentation, and can also be imported explicitly:
import type { IconName } from 'nuxt-svg-icon-module'
const icon = ref<IconName>('check')This gives you autocomplete and type-safety wherever you pass an icon name — in scripts, composables, or typed component props.
If no SVG files are found in iconsDir, IconName falls back to string.
How it works
The component uses Vite's import.meta.glob with the ?raw query to import SVG files as plain strings at build time. At runtime:
- The component calls
useAsyncDatawith a key unique per instance (viauseId) and icon name — so multiple<Icon name="check" />on the same page share one request but never conflict - The raw SVG string is injected via
v-htmlinside a<span>wrapper - If the SVG contains a
strokeattribute (andfilledis not set),.icon_strokeis applied — makingstrokeinheritcurrentColor. Otherwise.icon_fillis applied forfill - The
filledprop skips both classes, leaving the SVG colors untouched
Only icons that are actually rendered get bundled — unused SVGs are tree-shaken automatically.
Security note: SVG content is injected via
v-html. Never pass untrusted or user-supplied SVG strings through this component — only serve icons from your owniconsDir. Injecting arbitrary SVG can lead to XSS attacks.
Contributing
# clone and install
git clone https://github.com/markgareev/nuxt-svg-icon-module.git
cd nuxt-svg-icon-module
pnpm install
# prepare stubs and start playground
pnpm dev:prepare
pnpm dev
# run tests
pnpm test
# lint
pnpm lintPull requests are welcome. For major changes, please open an issue first.
