Access thousands of icons as components on-demand universally.
- 🌏 Universal
- 🤹 Any icon sets - ~150 popular sets with over 200,000 icons, logos, emojis, etc. Powered by Iconify.
- 📦 Major build tools - Vite, Webpack, Rollup, Nuxt, Rspack, etc. Powered by unplugin.
- 🚀 Major frameworks - Vanilla, Web Components, React, Vue 3, Vue Vapor, Solid, Svelte, and more. Contribute.
- 🍱 Any combinations of them!
- ☁️ On-demand - Only bundle the icons you really use, while having all the options.
- 🖨 SSR / SSG friendly - Ship the icons with your page, no more FOUC.
- 🌈 Stylable - Change size, color, or even add animations as you would with styles and classes.
- 📥 Custom icons - load your custom icons to get universal integrations at ease.
- 📲 Auto Importing - Use icons as components directly in your template.
- 🦾 TypeScript support.
- 🔍 Browse Icons
💡 Story behind this tool: Journey with Icons Continues - a blog post by Anthony |
vite-plugin-iconshas been renamed tounplugin-icons, see the migration guide
From v24.0.0
unplugin-iconsrequires Node 20 or above: unplugin v3.0.0 requires Node 20 or above.
Import icons using the convention ~icons/{collection}/{icon} and use them as components. Auto importing is also supported.
React Example:
import IconAccessibility from '~icons/carbon/accessibility'
import IconAccountBox from '~icons/mdi/account-box'
function App() {
return (
<div>
<IconAccessibility />
<IconAccountBox style={{ fontSize: '2em', color: 'red' }} />
</div>
)
}Vue Example:
<script setup>
import IconAccessibility from '~icons/carbon/accessibility'
import IconAccountBox from '~icons/mdi/account-box'
</script>
<template>
<icon-accessibility />
<icon-account-box style="font-size: 2em; color: red" />
</template>Note: This package is ESM-only. Make sure your project uses ES modules (
"type": "module"inpackage.jsonor.mjsfile extensions).
npm i -D unplugin-iconsWe use Iconify as the icons data source (supports 100+ icon sets).
Tip
✨ VS Code Users: Install the Iconify IntelliSense extension for inlay preview, auto-completion, and hover information.
Option A: Install Full Collection (Recommended for flexibility)
npm i -D @iconify/jsonThis installs all icon sets (~120MB). Only icons you actually use will be bundled in production.
Option B: Install Individual Icon Sets
Install only the icon sets you need:
npm i -D @iconify-json/mdi @iconify-json/carbonOption C: Auto Install (Experimental)
Let unplugin-icons automatically install icon sets when you import them:
Icons({
autoInstall: true, // Auto-detects npm/yarn/pnpm
})Check out the playgrounds page to try examples online in StackBlitz.
Available examples:
- Vite + Vue 3
- Vite + React
- Next.js
- Nuxt 4
- SvelteKit
- Astro
- And more...
This section covers how to configure unplugin-icons for different build tools and frameworks.
Vite
// vite.config.ts
import Icons from 'unplugin-icons/vite'
export default defineConfig({
plugins: [
Icons({ /* options */ }),
],
})Rollup
// rollup.config.js
import Icons from 'unplugin-icons/rollup'
export default {
plugins: [
Icons({ /* options */ }),
],
}Webpack
// webpack.config.mjs
import Icons from 'unplugin-icons/webpack'
export default {
/* ... */
plugins: [
Icons({ /* options */ }),
],
}Nuxt
Nuxt 2 and Nuxt Bridge
// nuxt.config.ts
export default {
buildModules: [
['unplugin-icons/nuxt', { /* options */ }],
],
}Nuxt 3/4
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
['unplugin-icons/nuxt', { /* options */ }]
],
})Or work with unplugin-vue-components resolvers
import IconsResolver from 'unplugin-icons/resolver'
import ViteComponents from 'unplugin-vue-components/vite'
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'unplugin-icons/nuxt',
],
vite: {
plugins: [
ViteComponents({
resolvers: [
IconsResolver({/* options */}),
],
}),
],
},
})See the Nuxt example for a working example project.
Rspack
import Icons from 'unplugin-icons/rspack'
// rspack.config.mjs
export default defineConfig({
plugins: [
// ...
Icons({/* options */}),
]
})Vue CLI
Note: This package is ESM-only. You need to use
vue.config.mjswith ES module syntax (requires@vue/cli-service ^5.0.8).
// vue.config.mjs
import Icons from 'unplugin-icons/webpack'
export default {
configureWebpack: {
plugins: [
Icons({ /* options */ }),
],
},
}SvelteKit
Add to your vite.config.ts:
import { sveltekit } from '@sveltejs/kit/vite'
import Icons from 'unplugin-icons/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
sveltekit(),
Icons({
compiler: 'svelte',
})
]
})Check instructions in the Frameworks -> Svelte section below if you faced module import errors.
See the SvelteKit example for a working example project.
Svelte + Vite
Svelte support requires the @sveltejs/vite-plugin-svelte plugin:
npm i -D @sveltejs/vite-plugin-svelteAdd to your vite.config.ts:
import { svelte } from '@sveltejs/vite-plugin-svelte'
import Icons from 'unplugin-icons/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
svelte(),
Icons({
compiler: 'svelte',
}),
],
})Check instructions in the Frameworks -> Svelte section below if you faced module import errors.
See the Svelte + Vite example for a working example project.
Next.js
Note: This package is ESM-only. You need to use
next.config.mjswith ES module syntax.
Add to your next.config.mjs:
// next.config.mjs
import Icons from 'unplugin-icons/webpack'
/** @type {import('next').NextConfig} */
export default {
reactStrictMode: true,
webpack(config) {
config.plugins.push(
Icons({
compiler: 'jsx',
jsx: 'react'
})
)
return config
}
}Check instructions in the Frameworks -> React section below if you faced module import errors.
.jsx extension to the import path, so that Next.js knows how to load it, by example:
import IconArrowRight from '~icons/dashicons/arrow-right.jsx';
// ^-- write `.jsx` to avoid
// https://github.com/antfu/unplugin-icons/issues/103
// ...some code later
<IconArrowRight />See the Next.js example for a working example project.
esbuild
// esbuild.config.js
import { build } from 'esbuild'
import Icons from 'unplugin-icons/esbuild'
build({
/* ... */
plugins: [
Icons({
/* options */
}),
],
})Astro
// astro.config.mjs
import { defineConfig } from 'astro/config'
import Icons from 'unplugin-icons/vite'
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [
Icons({
compiler: 'astro',
}),
],
},
})See the Astro example for a working example project.
Astro + Vue
Required @astrojs/vue installed.
import Vue from '@astrojs/vue'
// astro.config.mjs
import { defineConfig } from 'astro/config'
import Icons from 'unplugin-icons/vite'
// https://astro.build/config
export default defineConfig({
integrations: [
Vue(),
],
vite: {
plugins: [
Icons({
compiler: 'vue3',
}),
],
},
})See the Astro + Vue example for a working example project.
Configure the compiler option based on your framework. Some frameworks may require additional peer dependencies.
Vue 3
Configuration:
Icons({ compiler: 'vue3' })Peer Dependency:
Note: As of Vue 3.2.13+,
@vue/compiler-sfcis included in the mainvuepackage, so no additional installation is needed.
If you're using an older version:
npm i -D @vue/compiler-sfcTypeScript Support:
Add to your tsconfig.json:
See the Vue 3 example for a complete setup.
Vue Vapor
Emits Vapor mode components instead of virtual-DOM ones. An app mounted with createVaporApp and no VDOM interop plugin renders a virtual-DOM icon as nothing at all — no error, no warning — so a Vapor app needs this compiler rather than vue3.
Configuration:
Icons({ compiler: 'vue-vapor' })Peer Dependency:
Requires Vue 3.6+:
npm i -D @vue/compiler-vaporTypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/vue"
]
}
}React
Configuration:
Icons({ compiler: 'jsx', jsx: 'react' })Peer Dependencies:
npm i -D @svgr/core @svgr/plugin-jsxTypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/react"
]
}
}See the React example for a complete setup.
Preact
Configuration:
Icons({ compiler: 'jsx', jsx: 'preact' })Peer Dependencies:
npm i -D @svgr/core @svgr/plugin-jsxTypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/preact"
]
}
}See the Preact example for a complete setup.
Solid
Configuration:
Icons({ compiler: 'solid' })TypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/solid"
]
}
}See the Solid example for a complete setup.
Svelte
Configuration:
Icons({ compiler: 'svelte' })TypeScript Support:
For SvelteKit, add to src/app.d.ts:
import 'unplugin-icons/types/svelte'For Svelte + Vite, add to src/vite-env.d.ts:
/// <reference types="svelte" />
/// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/svelte" />For Svelte 4, use:
/// <reference types="unplugin-icons/types/svelte4" />For Svelte 3, use:
/// <reference types="unplugin-icons/types/svelte3" />See the Svelte example for a complete setup.
Astro
Configuration:
Icons({ compiler: 'astro' })TypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/astro"
]
}
}See the Astro example for a complete setup.
Astro + Vue
Configuration:
Icons({ compiler: 'vue3' })Requirements:
Requires @astrojs/vue to be installed.
TypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/vue"
]
}
}See the Astro + Vue example for a complete setup.
Qwik
Option 1: Native Qwik Compiler (Recommended)
Configuration:
Icons({ compiler: 'qwik' })Peer Dependency:
npm i -D @svgx/coreOption 2: JSX Compiler
Configuration:
Icons({ compiler: 'jsx', jsx: 'qwik' })Peer Dependencies:
npm i -D @svgr/core @svgr/plugin-jsxTypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/qwik"
]
}
}See the Qwik example for a complete setup.
Ember
Configuration:
Icons({ compiler: 'ember' })Build Tool Support:
Ember works with either Webpack or Vite.
For Vite applications, add to vite.config.mjs:
import { ember, extensions } from '@embroider/vite'
import { babel } from '@rollup/plugin-babel'
import Icons from 'unplugin-icons/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
ember(),
Icons({
compiler: 'ember',
}),
babel({
babelHelpers: 'runtime',
extensions,
}),
],
})TypeScript Support:
Add to your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/ember"
]
}
}Ember + Webpack
Assuming your app was generated with --embroider, or manually migrated to embroider following the instructions on the old embroider readme
Add the Icon plugin to the webpack plugins array in ember-cli-build.js:
import { compatBuild } from '@embroider/compat'
import Icons from 'unplugin-icons/webpack'
return compatBuild(app, Webpack, {
packagerOptions: {
webpackConfig: {
plugins: [
Icons({
compiler: 'ember',
}),
],
},
},
// ...other optionsSee the Ember (with Webpack) or Ember vite example for a working example project.
Available from
v0.13.2+
Import icons as raw SVG strings by adding ?raw to the import path. Useful for embedding SVG directly in HTML templates.
Example (Vue 3):
<script setup lang='ts'>
import RawMdiAlarmOff from '~icons/mdi/alarm-off?raw&width=4em&height=4em'
import RawMdiAlarmOff2 from '~icons/mdi/alarm-off?raw&width=1em&height=1em'
</script>
<template>
<!-- raw example -->
<pre>
import RawMdiAlarmOff from '~icons/mdi/alarm-off?raw&width=4em&height=4em'
{{ RawMdiAlarmOff }}
import RawMdiAlarmOff2 from '~icons/mdi/alarm-off?raw&width=1em&height=1em'
{{ RawMdiAlarmOff2 }}
</pre>
<!-- svg example -->
<span v-html="RawMdiAlarmOff" />
<span v-html="RawMdiAlarmOff2" />
</template>Load your own custom icons and use them with the same universal API.
import { promises as fs } from 'node:fs'
// loader helpers
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
Icons({
customCollections: {
// key as the collection name
'my-icons': {
account: '<svg><!-- ... --></svg>',
// load your custom icon lazily
settings: () => fs.readFile('./path/to/my-icon.svg', 'utf-8'),
/* ... */
},
'my-other-icons': async (iconName) => {
// your custom loader here. Do whatever you want.
// for example, fetch from a remote server:
return await fetch(`https://example.com/icons/${iconName}.svg`).then(res => res.text())
},
// a helper to load icons from the file system
// files under `./assets/icons` with `.svg` extension will be loaded as it's file name
// you can also provide a transform callback to change each icon (optional)
'my-yet-other-icons': FileSystemIconLoader(
'./assets/icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" '),
),
},
})Then use as
import IconAccount from '~icons/my-icons/account'
import IconFoo from '~icons/my-other-icons/foo'
import IconBar from '~icons/my-yet-other-icons/bar'💡 SVG Authoring Tips:
- To make your icons color adaptable, set
fill="currentColor"orstroke="currentColor"in your SVG.- Leave the
heightandwidthunspecified, we will set them for you.
When using auto-importing, register your custom collection names:
IconResolver({
customCollections: [
'my-icons',
'my-other-icons',
'my-yet-other-icons',
],
})See the Vue 3 example for a complete setup.
Load icons from third-party packages that follow the Iconify format.
Requirements:
External packages must include an icons.json file in IconifyJSON format. See Exporting icon set as JSON package for details.
For example, you can use an-awesome-collection or @my-awesome-collections/some-collection to load your custom or third party icons:
// loader helpers
import { ExternalPackageIconLoader } from 'unplugin-icons/loaders'
Icons({ customCollections: ExternalPackageIconLoader('my-awesome-collection') })When using with resolvers for auto-importing, remember you will need to tell it your custom collection names:
IconResolver({
customCollections: [
'my-awesome-collection',
],
})You can also combine it with FileSystemIconLoader or with other custom icon loaders:
// loader helpers
import { ExternalPackageIconLoader, FileSystemIconLoader } from 'unplugin-icons/loaders'
Icons({
customCollections: {
...ExternalPackageIconLoader('an-awesome-collection'),
...ExternalPackageIconLoader('@my-awesome-collections/some-collection'),
...ExternalPackageIconLoader('@my-awesome-collections/some-other-collection'),
'my-yet-other-icons': FileSystemIconLoader(
'./assets/icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" '),
),
},
})See the Vue 3 example for a complete setup.
Load icons from your local file system including HMR when you change your local SVG files.
Since unplugin-icons is built on top of unplugin, HMR should work out of the box with Vite, Webpack, and Rspack. You can also use it with other build tools that support HMR when running the dev server.
⚠️ Important HMR Limitations:
- Directory Location: Your local SVG folder must reside inside your project's root directory. If the folder is placed outside the root, the underlying bundler's file watcher may not detect file changes, causing HMR to fail.
- Ember + Webpack: Legacy watcher architectures (like Ember CLI with Broccoli) are extremely strict with their sandboxed environments. Your custom SVG folder must be placed inside a natively watched tree (such as
app/). If placed elsewhere, the file system events will be ignored by Broccoli and will never notify Webpack to invalidate the virtual module cache.
For example:
// loader helpers
import { FileSystemHMRIconLoader } from 'unplugin-icons/loaders'
Icons({ customCollections: FileSystemHMRIconLoader('./assets/my-local-svg-folder', 'my-awesome-collection') })When using with resolvers for auto-importing, remember you will need to tell it your custom collection names:
IconResolver({
customCollections: [
'my-awesome-collection',
],
})You can also combine it with FileSystemIconLoader or with other custom icon loaders:
// loader helpers
import { ExternalPackageIconLoader, FileSystemHMRIconLoader, FileSystemIconLoader } from 'unplugin-icons/loaders'
Icons({
customCollections: {
...ExternalPackageIconLoader('an-awesome-collection'),
...ExternalPackageIconLoader('@my-awesome-collections/some-collection'),
...FileSystemHMRIconLoader('./assets/my-local-svg-folder', 'my-awesome-collection'),
'my-yet-other-icons': FileSystemIconLoader(
'./assets/icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" '),
),
},
})See the Vue 3 example for a complete setup.
Customize individual icons or entire collections using iconCustomizer in your config or query parameters when importing.
Precedence: Query params > iconCustomizer > default configuration
Works with all icon sources: custom loaders, inlined collections, and Iconify collections.
For example, you can configure iconCustomizer to change all icons for a collection or individual icons on a collection:
import { promises as fs } from 'node:fs'
// loader helpers
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
Icons({
customCollections: {
// key as the collection name
'my-icons': {
account: '<svg><!-- ... --></svg>',
// load your custom icon lazily
settings: () => fs.readFile('./path/to/my-icon.svg', 'utf-8'),
/* ... */
},
'my-other-icons': async (iconName) => {
// your custom loader here. Do whatever you want.
// for example, fetch from a remote server:
return await fetch(`https://example.com/icons/${iconName}.svg`).then(res => res.text())
},
// a helper to load icons from the file system
// files under `./assets/icons` with `.svg` extension will be loaded as it's file name
// you can also provide a transform callback to change each icon (optional)
'my-yet-other-icons': FileSystemIconLoader(
'./assets/icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" '),
),
},
iconCustomizer(collection, icon, props) {
// customize all icons in this collection
if (collection === 'my-other-icons') {
props.width = '4em'
props.height = '4em'
}
// customize this icon in this collection
if (collection === 'my-icons' && icon === 'account') {
props.width = '6em'
props.height = '6em'
}
// customize this @iconify icon in this collection
if (collection === 'mdi' && icon === 'account') {
props.width = '2em'
props.height = '2em'
}
},
})or you can use query params to apply to individual icons:
<script setup lang='ts'>
import MdiAlarmOff from 'virtual:icons/mdi/alarm-off?width=4em&height=4em'
import MdiAlarmOff2 from 'virtual:icons/mdi/alarm-off?width=1em&height=1em'
</script>
<template>
<!-- width=4em and height=4em -->
<mdi-alarm-off />
<!-- width=4em and height=4em -->
<MdiAlarmOff />
<!-- width=1em and height=1em -->
<MdiAlarmOff2 />
</template>See the Vue 3 example for a complete implementation.
Apply transformations to all custom icons during loading. Useful for adding default attributes like fill="currentColor".
Icons({
customCollections: {
// key as the collection name
'my-icons': {
account: '<svg><!-- ... --></svg>',
/* ... */
},
},
transform(svg, collection, icon) {
// apply fill to this icon on this collection
if (collection === 'my-icons' && icon === 'account')
return svg.replace(/^<svg /, '<svg fill="currentColor" ')
return svg
},
})When using this plugin with your custom icons, consider using a cleanup process similar to that done by Iconify for any icons sets. All the tools you need are available in Iconify Tools.
You can check this repo, using unplugin-icons on a SvelteKit project: https://github.com/iconify/tools/tree/main/%40iconify-demo/unplugin-svelte.
Read Cleaning up icons article from Iconify for more details.
If you're upgrading from vite-plugin-icons, follow these steps:
1. Update package.json:
{
"devDependencies": {
- "vite-plugin-icons": "*",
+ "unplugin-icons": "^0.7.0",
}
}2. Update your config file:
import Components from 'unplugin-vue-components/vite'
- import Icons, { ViteIconsResolver } from 'vite-plugin-icons'
+ import Icons from 'unplugin-icons/vite'
+ import IconsResolver from 'unplugin-icons/resolver'
export default {
plugins: [
Vue(),
Components({
resolvers: [
IconsResolver()
],
}),
Icons(),
],
}3. Update import paths:
- import IconComponent from 'virtual:vite-icons/collection/name'
+ import IconComponent from '~icons/collection/name'Note: The
virtual:iconsprefix still works in Vite, but~iconsis recommended for consistency across all build tools.
Configure default styling and behavior for all icons:
Icons({
// Icon sizing
scale: 1.2, // Scale factor relative to 1em (default: 1.2)
// Default styling
defaultStyle: '', // CSS styles applied to all icons
defaultClass: '', // CSS classes applied to all icons
// Compiler configuration
compiler: null, // Framework compiler: 'vue3', 'vue-vapor', 'jsx', 'svelte', 'solid', etc.
jsx: 'react', // JSX framework: 'react' or 'preact' (when compiler: 'jsx')
// Custom collections
customCollections: {}, // See [Custom Icons](#custom-icons)
// Advanced
iconCustomizer: () => {}, // See [Icon Customization](#icon-customization)
transform: undefined, // See [Global Icon Transformation](#global-icon-transformation)
autoInstall: false, // Auto-install icon sets on import
})Vue 3
Use with unplugin-vue-components
For example in Vite:
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
export default {
plugins: [
Vue(),
Components({
resolvers: [
IconsResolver(),
],
}),
Icons(),
],
}Then you can use any icons as you want without explicit importing. Only the used icons will be bundled.
<template>
<i-carbon-accessibility/>
<i-mdi-account-box style="font-size: 2em; color: red"/>
</template>React & Solid
Use with unplugin-auto-import
For example in Vite:
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
export default {
plugins: [
AutoImport({
resolvers: [
IconsResolver({
prefix: 'Icon',
extension: 'jsx',
}),
],
}),
Icons({
compiler: 'jsx', // or 'solid'
}),
],
}Then you can use any icons with the prefix Icon as you want without explicit importing. Type declarations will be generated on the fly.
export function Component() {
return (
<div>
<IconCarbonApps />
<IconMdiAccountBox style="font-size: 2em; color: red" />
</div>
)
}Icons are auto-imported following this naming pattern:
{prefix}-{collection}-{icon}
prefix: Component name prefix (default:i)collection: Iconify collection ID (e.g.,mdi,carbon,fa-solid)icon: Icon name (kebab-case)
Custom Prefix:
IconsResolver({
prefix: 'icon', // Use 'icon' instead of 'i'
})<template>
<icon-mdi-account />
</template>No Prefix:
IconsResolver({
prefix: false,
enabledCollections: ['mdi'], // Optional: limit to specific collections
})<template>
<mdi-account />
</template>Create shorter aliases for long collection names:
IconsResolver({
alias: {
park: 'icon-park', // Use <icon-park-* /> instead of <icon-icon-park-* />
fas: 'fa-solid', // Use <icon-fas-* /> instead of <icon-fa-solid-* />
}
})Both the alias and full collection name work:
<template>
<icon-park-abnormal /> <!-- Using alias -->
<icon-icon-park-abnormal /> <!-- Using full name -->
</template>This project is part of my Sponsor Program
MIT License © 2020-PRESENT Anthony Fu
{ "compilerOptions": { "types": [ "unplugin-icons/types/vue" ] } }