Skip to content

Repository files navigation

nuxt-icon

Nuxt Icon

npm versionnpm downloadsLicenseNuxtVolta board

Add 200,000+ ready to use icons to your Nuxt application, based on Iconify.

Features ✨

  • Nuxt 3 ready
  • SSR friendly
  • Support 200,000 open-source vector icons via Iconify
  • Support both CSS mode / SVG mode
  • Custom SVG support (via Vue component, or via local SVG files)

Note

You are viewing the v1.0 version of this module, which is a complete rewrite for a better developer experience and performance. If you are migrating from v0.6, please check this PR for the full list of changes.

Setup ⛓️

Run the following command to add the module to your project:

npx nuxi module add icon

That's it, you can now use the <Icon /> in your components!

✨ If you are using VS Code, you can use the Iconify IntelliSense extension by @antfu

Manual Setup

You can install the module manually with:

npm i @nuxt/icon

Update your nuxt.config.ts

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon']})

If you have the legacy module nuxt-icon installed, you might want to remove it from the modules list.

Usage 👌

Props:

  • name (required): icon name or global component name
  • size: icon size (default: 1em)
  • mode: icon rendering mode (svg or css, default: css)

Attributes:

When using an icon from Iconify, a <span> or <svg> will be created based on the rendering mode, you can give all the attributes of the native element.

<Iconname="uil:github" style="color: black" />

TailwindCSS v4:

When using TailwindCSS v4 with the css mode, you should configure the cssLayer in Nuxt's app config:

// ~/app.config.tsexportdefaultdefineAppConfig({icon: {mode: 'css',cssLayer: 'base'}})

Iconify Dataset

You can use any name from the https://icones.js.org collection:

<Iconname="uil:github" />

It supports the i- prefix (for example, i-uil-github).

It's highly recommended to install the icon data locally with

npm i -D @iconify-json/collection-name

For example, to use the uil:github icon, install its collection with @iconify-json/uil. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side.

Note

You may also know you can install @iconify/json package to include all iconify icons. This is not recommended because it will increase your server bundle size and building performance. If you choose to do so, we'd recommend to explicitly specify the collection names you need:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {serverBundle: {collections: ['uil','mdi']// <!--- this}}})

Vue Component

When the name matches a global registered component, it will be rendered as that component (in this case mode will be ignored):

<Iconname="MyComponent" />

Note that MyComponent needs to be inside components/global/ folder (see example).

Tip

You can also change the component name with:

exportdefaultdefineNuxtConfig({icon: {componentName: 'NuxtIcon'}})

Custom Local Collections

You can use local SVG files to create a custom Iconify collection.

For example, place your icons' SVG files under a folder of your choice, for example, ./assets/my-icons:

assets/my-icons
├── foo.svg
├── bar-outline.svg

In your nuxt.config.ts, add an item in icon.customCollections:

import{createResolver}from"nuxt/kit"const{ resolve }=createResolver(import.meta.url)exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {customCollections: [{prefix: 'my-icon',dir: resolve('./assets/my-icons'),// if you want to include all the icons in nested directories:// recursive: true,},],},})

Note

We use createResolver and resolve() to ensure paths work correctly both in regular projects and when your project is used as an extendable layer by other Nuxt projects. This ensures icon paths resolve relative to your project's directory rather than the consuming project's directory.

Note

If you are running on Nuxt 4 with the new app directory, the assets directory is './app/assets/*' instead of './assets/*'.

Then you can use the icons like this:

<template>
<Icon name="my-icon:foo" />
<Icon name="my-icon:bar-outline" />
</template>

You can also pass a full custom IconifyJSON object:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {customCollections: [{prefix: 'paid-icons',icons: {'nuxt': {body: '<path d="M281.44 ... />'},},width: 512,height: 512,}],},})

Note that custom local collections require you to have a server to serve the API. When setting ssr: false, or when generating a static app using nuxt generate (which is equivalent to ssr: false), the provider will default to the Iconify API (which does not have your custom icons). If you want to build a SPA with server endpoints, you can explicitly set provider: 'server':

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],ssr: false,icon: {provider: 'server',// <-- thiscustomCollections: [{prefix: 'my-icon',dir: './assets/my-icons'},],},})

Disabling Runtime Fetching

Or if you want to disable the dynamic icon fetching completely and only use icons from the client bundle, you can set provider: 'none':

exportdefaultdefineNuxtConfig({icon: {provider: 'none',clientBundle: {scan: true,// ...or other bundle options},}})

Case Sensitive Custom Collections

Before v1.10, due to the limitation of Iconify's previous convention, all custom icons were normalized to kebab-case with a warning. Thanks to the updates on Iconify side, starting from v1.10, you can opt-in to use case-sensitive custom collections and by pass the normalization.

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {customCollections: [{prefix: 'my-icon',dir: './assets/my-icons',normalizeIconName: false,// <-- this},],},})

Which enable to use assets/my-icons/FooBar.svg as my-icon:FooBar, for example.

normalizeIconName is default to true for backward compatibility, and will be flipped in the future major version. See #265 for more context.

Icon Customization

To update the default size (1em) of the <Icon />, create an app.config.ts with the icon.size property.

Update the default class (.icon) of the <Icon /> with the icon.class property, for a headless Icon, set icon.class: ''`.

You can also define aliases to make swapping out icons easier by leveraging the icon.aliases property.

Note

Note it's app.config.ts and not nuxt.config.ts for runtime configs.

// app.config.tsexportdefaultdefineAppConfig({icon: {size: '24px',// default <Icon> size appliedclass: 'icon',// default <Icon> class appliedmode: 'css',// default <Icon> mode appliedaliases: {'nuxt': 'logos:nuxt-icon',},cssLayer: 'base'// set the css layer to inject to}})

The icons will have the default size of 24px and the nuxt icon will be available:

<Iconname="nuxt" />

By default, this module will create a server endpoint /api/_nuxt_icon/:collection to serve the icons from your local server bundle (you can override the default path by setting icon.localApiEndpoint to your desired path). When requesting an icon that does not exist in the local bundle, it will fallback to requesting the official Iconify API. You can disable the fallback by setting icon.fallbackToApi to false, or set up your own Iconify API and update icon.iconifyApiEndpoint to your own API endpoint.

Customizing Icons with the customize Option

The customize option allows you to modify various aspects of the SVG icons used in your project. With this option, you can:

  • Change Stroke Width
  • Change Colors
  • Change Animation Duration
  • Change Opacity
  • Add Extra Shapes

You have full control over SVG content with these customization options.

In a Component You can define a customize function within a component to apply various modifications to your icons.

<script setup lang="ts">// Define the customize function to modify SVG contentconst customize = (content:string, name:string, prefix:string, provider:string) => {if (prefix!=='tabler') returncontent// Ignore Prefixreturncontent .replace(/stroke-width="[^"]*"/g, `stroke-width="2"`) // Change stroke width to 2 .replace(/stroke="[^"]*"/g, `stroke="#FF5733"`) // Change stroke color to red .replace(/fill="[^"]*"/g, `fill="#FF5733"`) // Change fill color to red .replace(/animation-duration="[^"]*"/g, `animation-duration="1s"`) // Change animation duration to 1s (for animated icons) .replace(/opacity="[^"]*"/g, `opacity="0.8"`);// Change opacity to 0.8}</script>
<template>
<Icon name="tabler:star" :customize="customize" />
</template>
<!-- You can also use `:customize="false"` to disabled the global customization function per-usage -->

In the App Configuration File:

Alternatively, you can apply these customizations globally in the app.config.ts file.

// app.config.tsexportdefaultdefineAppConfig({icon: {customize: (content: string,name: string,prefix: string,provider: string)=>{// ...},}})

With this configuration, all icons throughout your application will have these customizations applied consistently.

Server Bundle

Since @nuxt/icon v1.0, we have introduced the server bundle concept to serve the icons from Nuxt server endpoints. This keeps the client bundle lean and able to load icons on-demand, while having all the dynamic features to use icons that might not be known at build time.

Server Bundle Mode: local

This mode will bundle the icon collections you have installed locally (like @iconify-json/*), into your server bundle as dynamic chunks. The collection data will be loaded on-demand, only when your client request icons from that collection.

Server Bundle Mode: remote

Introduced in @nuxt/icon v1.2, you can now use the remote server bundle to serve the icons from a remote CDN.

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {serverBundle: 'remote',},})

Or you can specify the remote provider:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {serverBundle: {remote: 'jsdelivr',// 'unpkg' or 'github-raw', or a custom function}},})

Which will make server requests to https://cdn.jsdelivr.net/npm/@iconify-json/ph/icons.json to fetch the icons at runtime, instead of bundling them with your server.

Under the hood, instead of bundling () => import('@iconify-json/ph/icons.json') to your server bundle, it will now use something like () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/ph/icons.json').then(res => res.json()), where the collections are not inlined.

This would be useful when server bundle size is a concern, like in serverless or worker environments.

Server Bundle Mode: auto

This is the default option, where the module will pick between local and remote based your deployment environment. local will be preffered unless you are deploying to a serverless or worker environment, like Vercel Edge or Cloudflare Workers.

Externalize Icons JSON

By default, Nitro will bundle the icon collections you have installed locally (like @iconify-json/*), into your server bundle as dynamic chunks. When you have a large number of icons, this might make your bundling process slow and memory-intensive. You can change to externalize the icons JSON files by setting icon.serverBundle.externalizeIconsJson to true.

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {serverBundle: {externalizeIconsJson: true,}},})

Note that this will require your production Node.js server to be able to import JSON files (Note that as in Node.js v22, JSON modules are still an experimental feature). In the final build, it will contain statements like () => import('@iconify-json/ph/icons.json', { with: { type: 'json' } }).

Also note that in some serverless environments, like Cloudflare Workers, where they don't have dynamic imports, they will always be inlined regardless of this option.

This option will be ignored when icon.serverBundle.remote is enabled.

Completely Disable Server Bundle

If you want to disable the server bundle completely, you can set icon.serverBundle to false and provider to iconify

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {provider: 'iconify',serverBundle: false,},})

This will make requests to Iconify API every time the client requests an icon. We do not recommend doing so unless the other options are not feasible.

Client Bundle

For icons that you know you are going to use frequently, you can bundle them with your client bundle to avoid network requests.

Prefetch (Preload) Icons

If you want icons to be available on first render, use the client bundle as the prefetch mechanism:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon',],icon: {clientBundle: {// Explicitly pre-bundle known iconsicons: ['uil:github','heroicons:home'],// Keep disabled if you only want explicit entriesscan: false,},},})

You can also enable static scanning to pre-bundle icons referenced literally in your source files:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon',],icon: {clientBundle: {// Automatically includes statically-detected icon usagesscan: true,// Keep explicit entries for dynamic icon namesicons: ['uil:github'],},},})

If you want to disable runtime icon fetching entirely, see Disabling Runtime Fetching for the provider: 'none' + client bundle pattern.

Tip

Static scanning only detects literal icon names. See Scan Components for details and examples, and Rendering Icons in Component Tests for test-environment setup.

Available Options

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {clientBundle: {// list of icons to include in the client bundleicons: ['uil:github','logos:vitejs'],// scan all components in the project and include icons scan: true,// include all custom collections in the client bundleincludeCustomCollections: true,// guard for uncompressed bundle size, will fail the build if exceedssizeLimitKb: 256,},},})

includeCustomCollections will include all the custom collections you have defined in icon.customCollections in the client bundle. It's disabled by default but will automatically enable when ssr: false is set.

Scan Components

When scan is enabled, the module will scan all the components in your project and include the icons used in the client bundle. This would significantly reduce the number of network requests needed for statically known icons, but might also increase the client bundle size depending on the number of icons used in your project.

You can also fine-tune tine scanning targets like:

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon'],icon: {clientBundle: {scan: {// note that when you specify those values, the default behavior will be overriddenglobInclude: ['components/**/*.vue',/* ... */],globExclude: ['node_modules','dist',/* ... */],},},},})

Tip

Scanning is relying on static analysis, which means only literal usages will be detected. Avoid constructing the icon name dynamically whenever possible.

<template>
<!-- Avoid this -->
<Icon :name="`carbon:${dark?'moon':'sun'}`" />
<!-- Prefer this -->
<Icon :name="dark?'carbon:moon':'carbon:sun'" />
</template>

Standalone Vite Plugin (without Nuxt)

If you are building a plain Vue app with Vite (for example when using Nuxt UI's Vue integration), there is no Nuxt server to serve icons from, and @iconify/vue would fetch icons from the Iconify API at runtime. To render icons offline (and during SSR) without network requests, @nuxt/icon ships a standalone Vite plugin that pre-bundles the icons you use into the client build:

// vite.config.tsimport{defineConfig}from'vite'import{NuxtIconBundle}from'@nuxt/icon/vite'exportdefaultdefineConfig({plugins: [NuxtIconBundle({// Explicitly pre-bundle known icons (hard-fails the build when missing locally)icons: ['uil:github','heroicons:home'],// Scanning your source files for icon usages is enabled by defaultscan: true,}),],})

Then register the bundled icons once in your entry file:

// main.tsimport'virtual:nuxt-icon-bundle/register'

Make sure the icon collections you use are installed locally (e.g. npm i -D @iconify-json/uil), and add the type shims for the virtual modules to your tsconfig.json:

{
"compilerOptions": {
"types": ["@nuxt/icon/client"]
}
}

The plugin accepts the same options as icon.clientBundle (icons, scan, sizeLimitKb), plus customCollections — with two differences in defaults: scan is enabled by default (bundling is the whole point of the plugin), and includeCustomCollections is always enabled by default (there is no server provider to fall back to).

For library and framework authors integrating icons themselves: virtual:nuxt-icon-bundle exports init(addIcon) so you can register the bundled data on your own copy of @iconify/vue (or any compatible store), and the underlying primitives (IconUsageScanner, resolveBundleIcons, generateClientBundleCode, ...) are exported from @nuxt/icon/utils.

See playgrounds/vite/ for a working example (pnpm play:vite to run it locally).

Render Function

You can use the Icon component in a render function (useful if you create a functional component), for this you can import it from #components:

import{Icon}from'#components'

See an example of a <MyIcon> component:

<script setup>import { Icon } from'#components'constMyIcon=h(Icon, { name:'uil:twitter' })</script>
<template>
<p><MyIcon /></p>
</template>

Rendering Icons in Component Tests

In in-browser component testing environments (such as Vitest Browser Mode or Cypress Component Testing), the internal Nuxt server routes used to fetch icons on demand are not available. As a result, icons may fail to render during tests.

To ensure icons render correctly in component tests, configure @nuxt/icon to use the client bundle when running in test mode.

Note Projects using @nuxt/ui must do this to see any UI icons during component testing.

Requirements

  • Install the icon collections you use locally (for example, @iconify-json/lucide).
  • Icons will not be fetched remotely when using the client bundle.

Test-only Configuration

Conditionally switch to the client bundle in your Nuxt config when NODE_ENV === 'test':

exportdefaultdefineNuxtConfig({modules: ['@nuxt/icon',],icon: process.env.NODE_ENV!=='test'
? {// Production or development icon configuration}
: {// Disable all network icon fetching in component testsprovider: 'none',clientBundle: {// Explicitly include dynamically constructed iconsicons: ['lucide:check'],// Scan your app and Nuxt UI runtime for static icon usagescan: {globInclude: ['{app,shared}/**','node_modules/@nuxt/ui/dist/**',],globExclude: ['node_modules'],},},},})

Tips

  • Dynamically generated icon names may not be detected by static scanning—add them explicitly to icons.
  • For large apps, consider moving test-only configuration into vitest.config.ts or cypress.config.ts for clarity.

Contributing 🙏

  1. Clone this repository
  2. Install dependencies using pnpm install (install pnpm with corepack enable, learn more)
  3. Run npm run dev:prepare to generate type stubs.
  4. Use npm run dev to start playground in development mode.

Credits 💌

License 📎

MIT License

About

The <Icon> component, supporting Iconify, Emojis and custom components.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1.2k stars

Watchers

4 watching

Forks

Releases

Used by

Contributors

Languages