# nuxt/icon resolves one component name against Iconify, your SVGs or your components

> The Nuxt Icon module gives a Nuxt application a single Icon component that can resolve a name against more than 200,000 Iconify vector icons, against SVG files in a local directory, or against a global Vue component, with a CSS rendering mode by default and an SVG mode for full control. It is MIT licensed, on version 2.5.1, and the interesting decisions are all about where the icon data comes from at runtime.

**nuxt/icon** — The <Icon> component, supporting Iconify, Emojis and custom components.

- Repository: https://github.com/nuxt/icon
- Website: https://stackblitz.com/edit/nuxt-icon-playground?file=app.vue
- Stars: 1,201 · Forks: 101
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/nuxt-icon

## One component, three sources

The module's job is small and its design choices are the interesting part. It adds 200,000 or more ready to use icons to a Nuxt application, based on Iconify, and it does it through one component. The name prop is required and is either an icon name or the name of a global component, and the resolution order is what makes the component worth having. An Iconify name such as uil:github renders from the Iconify data. A name that matches a global Vue component renders that component instead, with the mode ignored, and the component has to live in a components/global/ directory. And a name that matches a local SVG collection renders your own file. The size prop defaults to 1em, and the mode prop is svg or css with css as the default. The practical effect is that a designer hands you a name and the component figures out where it comes from, which removes the usual argument about which icon package to install.

## CSS mode by default, SVG mode when you need the element

The rendering mode determines what ends up in the DOM. When using an icon from Iconify, a span or an svg will be created based on the rendering mode, and in either case you can give all the attributes of the native element, so the SVG attributes documented by the MDN apply. The example in the documentation uses the style attribute directly on the component:

```html
<Icon name="uil:github" style="color: black" />
```

The default of css mode is worth understanding, because it is a performance decision rather than a cosmetic one. A CSS mode icon is a span with a background image, which means it inherits currentColor and can be recoloured with CSS, while an SVG mode icon is an inline element whose fill or stroke you control through its attributes.

## Install the collections you use, not the whole icon universe

You can use any name from the Iconify collection, and the i- prefix is supported, so i-uil-github works as well as uil:github. But the documentation is emphatic that it is highly recommended to install the icon data locally:

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

For the uil:github icon, that means installing the collection as @iconify-json/uil, and the reason given is that the icons can then be served locally or from your own serverless functions, which is faster and more reliable on both SSR and the client side. The alternative, installing the @iconify/json package to include all Iconify icons, is explicitly not recommended because it increases the server bundle size and the build performance cost. If you do choose it, the recommendation is to list the collection names you need explicitly under the serverBundle.collections option in the configuration, for example a list of uil and mdi. That is the same trade every icon pipeline makes, the difference between fetching an icon at runtime and shipping it, and the documentation puts the decision in your hands with a clear recommendation for the local route.

## Custom collections, and why the resolver matters

You can build a custom Iconify collection out of local SVG files, and the configuration is a prefix plus a directory. The icons go in a folder of your choice, for example ./assets/my-icons, containing files like foo.svg and bar-outline.svg, and the configuration lists the collection with a prefix and a resolved directory:

```ts
import { createResolver } from "nuxt/kit"

const { resolve } = createResolver(import.meta.url)

export default defineNuxtConfig({
  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,
      },
    ],
  },
})
```

The reason for createResolver and resolve() is explained in the documentation and it is a real one, that the paths work correctly both in regular projects and when your project is used as an extendable layer by other Nuxt projects, because the icon paths resolve relative to your project's directory rather than the consuming project's directory. A layer is a Nuxt feature where a project is consumed by another, and a path resolved from the wrong base directory is exactly the bug that appears only in the layered case. There is also a Nuxt 4 note, that with the new app directory the assets directory is ./app/assets/* rather than ./assets/*. Once the collection is registered, the icons are used with the prefix, as my-icon:foo and my-icon:bar-outline.

## The provider decides where icons come from at runtime

The provider setting is the setting to understand before you ship. Custom local collections require you to have a server to serve the API, and when you set ssr: false, or when you generate a static app with nuxt generate, which is equivalent to ssr: false, the provider will default to the Iconify API, which does not have your custom icons. The fix is to set the provider to server explicitly:

```ts
export default defineNuxtConfig({
  modules: ['@nuxt/icon'],
  ssr: false,
  icon: {
    provider: 'server', // <-- this
    customCollections: [
      {
        prefix: 'my-icon',
        dir: './assets/my-icons'
      },
    ],
  },
})
```

And if you want to disable the dynamic icon fetching completely and use only icons from the client bundle, you set provider to none, with the client bundle scan enabled. The third option is to pass a full custom IconifyJSON object, which lets you inline icon bodies directly, with a width and a height and a body string, as the paid-icons example in the documentation shows. So there are three providers, the default which fetches from the Iconify API, server which fetches from your own endpoints, and none which fetches nothing.

## Tailwind v4, the module rewrite and the repository layout

The Tailwind integration has one configuration point. When using TailwindCSS v4 with the css mode, you should configure the cssLayer in Nuxt's app config, with the icon mode set to css and the cssLayer set to base:

```ts
// ~/app.config.ts
export default defineAppConfig({
  icon: {
    mode: 'css',
    cssLayer: 'base'
  }
})
```

The layer matters because Tailwind's layers determine which styles win, and an icon that sits in the wrong layer can be overridden by a base style. The component name itself is configurable with the componentName option, which the documentation suggests for people who need to change it, with the default shown as NuxtIcon. And the module carries a migration note, that the version being viewed is the v1.0 version of the module, a complete rewrite for a better developer experience and performance, with a link to the pull request listing the full changes for anyone migrating from v0.6. The repository is a Nuxt module in TypeScript, with the package published as @nuxt/icon, the source in src/, a playgrounds/ directory with a Nuxt and a Vite playground, a test/ directory, an eslint config, a vitest config, and scripts whose test command runs the unit tests, the playground tests, a playground build and a Nuxt 4 fixture build, so the module is tested against more than one Nuxt generation.

## Conclusion

nuxt/icon is worth adding to a Nuxt application the moment a second icon library appears in the project, because the component hides whether the icon came from Iconify, from a local folder or from your own component, and because the local collection install is a one line package rather than a build step. It is the wrong tool if you need the icons to be in the bundle and nothing else, since the default provider fetches at runtime and the client bundle scan has to be configured for that. Before adopting it, install the collections you actually use as local packages rather than the whole @iconify/json bundle, because the documentation is explicit that the full package increases the server bundle size and the build time, and if you plan to use custom local collections in a static build, read the provider section first, since a static site falls back to the Iconify API and will not know about your icons.

## FAQ

### How do I add the Icon component to a Nuxt project?

Run npx nuxi module add icon, or install @nuxt/icon with npm and add it to the modules array in nuxt.config.ts. You can then use the Icon component in your components, and the Iconify IntelliSense extension for VS Code gives you the names.

### What are the props of the nuxt/icon Icon component?

The name prop is required and is an icon name or a global component name, the size prop defaults to 1em, and the mode prop selects the rendering mode, svg or css, with css as the default. When the name matches a global registered component it is rendered as that component and the mode is ignored.

### How do I serve icon data locally instead of from the Iconify API?

Install the collections you use as local packages, for example npm i -D @iconify-json/uil, or list the collection names under serverBundle.collections. Installing the whole @iconify/json package is not recommended because it increases the server bundle size and the build time.

### Do custom local collections work in a static Nuxt build?

Not by default. Custom local collections require a server to serve the API, and with ssr: false or a nuxt generate build the provider falls back to the Iconify API, which does not have your icons. Set provider to 'server' explicitly, or use provider 'none' with only the client bundle icons.

## Sources

- [License: MIT](https://github.com/nuxt/icon/blob/main/LICENSE)
- [nuxt/icon on GitHub](https://github.com/nuxt/icon)
- [Project website](https://stackblitz.com/edit/nuxt-icon-playground?file=app.vue)
- [README](https://github.com/nuxt/icon/blob/main/README.md)
- [Releases](https://github.com/nuxt/icon/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nuxt-icon
