SVG Icons in Vue 3: The Complete Guide (lucide-vue-next, Iconify & unplugin-icons)
How to use SVG icons in Vue 3 and Nuxt 3 — lucide-vue-next, @iconify/vue, and unplugin-icons with auto-import. Code patterns, tree-shaking, and dark mode.
Vue 3’s Composition API and Nuxt 3’s auto-import system make SVG icon integration cleaner than in any previous Vue version. Three tools dominate the ecosystem: lucide-vue-next for a Lucide-equivalent experience, @iconify/vue for access to 250,000+ icons, and unplugin-icons for automatic on-demand icon imports.
This guide covers all three with Vue 3 and Nuxt 3 patterns.
Option 1: lucide-vue-next
lucide-vue-next is the official Lucide port for Vue 3. Same 1,500+ icons, same MIT license, same prop API — but exported as Vue SFCs.
npm install lucide-vue-next
Usage in Vue 3
<script setup>
import { Search, Settings, Bell } from 'lucide-vue-next';
</script>
<template>
<div class="flex gap-4 text-gray-700 dark:text-gray-300">
<Search :size="20" />
<Settings :size="20" />
<Bell :size="20" :stroke-width="1.5" />
</div>
</template>
Vue templates use kebab-case for props: :stroke-width="1.5" not strokeWidth. The <script setup> import is the same as React.
Props reference
| Prop | Type | Default | Notes |
|---|---|---|---|
size | number | 24 | Width and height |
color | string | currentColor | SVG stroke color |
stroke-width | number | 2 | Stroke width |
class | string | — | CSS class |
Global registration in Nuxt 3
For icons used app-wide, register them as global components in a plugin:
// plugins/icons.ts
import { defineNuxtPlugin } from '#app';
import { Search, Bell, Settings, User } from 'lucide-vue-next';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('IconSearch', Search);
nuxtApp.vueApp.component('IconBell', Bell);
nuxtApp.vueApp.component('IconSettings', Settings);
nuxtApp.vueApp.component('IconUser', User);
});
Then use without importing in every component:
<template>
<IconSearch :size="18" />
</template>
Option 2: @iconify/vue
@iconify/vue gives you access to every icon in the Iconify ecosystem — Lucide, Tabler, Phosphor, Heroicons, Material Symbols, and 250,000+ more — through a single component.
npm install @iconify/vue
Basic usage
<script setup>
import { Icon } from '@iconify/vue';
</script>
<template>
<!-- Format: pack:icon-name -->
<Icon icon="lucide:search" class="w-5 h-5" />
<Icon icon="tabler:dashboard" class="w-5 h-5" />
<Icon icon="ph:heart-duotone" class="w-5 h-5 text-blue-500" />
</template>
Offline / SSR mode with bundled icons
By default, @iconify/vue fetches icons from the Iconify CDN at runtime. For SSR (Nuxt) or offline builds, bundle the icons you use:
npm install @iconify/json
# or install individual packs:
npm install @iconify-icons/lucide
<script setup>
import { Icon } from '@iconify/vue';
import searchIcon from '@iconify-icons/lucide/search';
</script>
<template>
<Icon :icon="searchIcon" class="w-5 h-5" />
</template>
@iconify/vue’s runtime API is fast and great for exploration. For a production app with a known icon set, switch to bundled icons or lucide-vue-next for zero runtime fetch overhead.
Option 3: unplugin-icons (recommended for Nuxt)
unplugin-icons is a Vite/Webpack plugin that auto-imports icons on demand — you reference them in templates, the plugin resolves and bundles only the icons you use, with no explicit import needed.
npm install -D unplugin-icons
npm install @iconify/json # icon data source
Vite setup (Vue 3)
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import Icons from 'unplugin-icons/vite';
import Components from 'unplugin-vue-components/vite';
import { IconsResolver } from 'unplugin-icons/resolver';
export default defineConfig({
plugins: [
vue(),
Icons({ compiler: 'vue3' }),
Components({
resolvers: [IconsResolver()],
}),
],
});
Nuxt 3 setup
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['unplugin-icons/nuxt'],
icons: {
// global config
},
});
Usage (auto-resolved, no imports)
With the resolver configured, components are named I{Pack}{IconName}:
<template>
<!-- Lucide: I + Lucide + IconName (PascalCase) -->
<ILucideSearch class="w-5 h-5" />
<ILucideSettings class="w-5 h-5" />
<!-- Tabler: I + Tabler + IconName -->
<ITablerDashboard class="w-5 h-5" />
<!-- Phosphor: I + Ph + IconName -->
<IPhHeart class="w-5 h-5 text-red-500" />
</template>
No import statement required. The plugin resolves and bundles only Search, Settings, Dashboard, and Heart — zero unused icons in the final bundle.
Add unplugin-icons/types/vue to your tsconfig.json includes for TypeScript to recognize the auto-generated component names.
Dark mode patterns in Vue
All three approaches use currentColor by default, so Tailwind’s dark mode works without extra configuration:
<template>
<!-- Tailwind dark: variant — icon inherits text color -->
<div class="text-gray-700 dark:text-gray-300">
<Search :size="20" />
</div>
</template>
For CSS variable-based theming:
<style>
:root { --icon-default: theme(colors.gray.700); }
.dark { --icon-default: theme(colors.gray.300); }
</style>
<template>
<Search :size="20" style="color: var(--icon-default)" />
</template>
Accessibility in Vue templates
<template>
<!-- Decorative icon in labeled button -->
<button>
<Download :size="18" aria-hidden="true" />
Export CSV
</button>
<!-- Standalone meaningful icon -->
<Search
:size="20"
role="img"
aria-label="Search"
/>
<!-- Icon-only button -->
<button aria-label="Delete item">
<Trash2 :size="18" aria-hidden="true" />
</button>
</template>
Choosing between the three approaches
| Need | Recommended |
|---|---|
| Lucide icons only, max tree-shaking | lucide-vue-next |
| Multiple icon packs in one project | @iconify/vue |
| Zero-import convenience in Nuxt/Vite | unplugin-icons |
| SSR-safe with bundled icons | @iconify/vue with offline mode |
| Smallest possible bundle | lucide-vue-next (named imports) |
Related Reading
- React SVG Icons – Best Free Libraries & How to Use Them
- SVG Icons for Next.js — Complete Guide & Best Packs
- Astro SVG Icons — Complete Guide
- Best Free SVG Icon Libraries 2026 – The Definitive Comparison
- SVG Icons in Dark Mode: The Complete Tailwind & CSS Guide
- Accessible SVG Icons: aria-label and role