Icons

Pohon UI integrates with Nuxt Icon to access over 200,000+ icons from Iconify.

Usage

Pohon UI automatically registers the @nuxt/icon module for you, so there's no additional setup required.

Icon component

You can use the Icon component with a name prop to display an icon:

<template>
  <PIcon name="i-lucide-lightbulb" class="size-5" />
</template>
You can use any name from the https://iconify.design collection. Browse them easily on https://icones.js.org.

Component props

Some components also have an icon prop to display an icon, like the Button for example:

<template>
  <PButton icon="i-lucide-sun" variant="subtle">Button</PButton>
</template>

Collections

Iconify dataset

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

pnpm i @iconify-json/{collection_name}

For example, to use the i-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.

Installing the collection also lets Pohon UI embed the icons it uses into the client bundle at build time, so they're available on first render instead of being loaded on demand at runtime. This happens automatically for Nuxt UI's own icons, from the lucide collection by default, and for the icons you set in your app.config.ts. To bundle other icons, such as ones you use elsewhere in your app, add them to @nuxt/icon's clientBundle.icons option in your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['pohon-ui'],
  css: ['~/assets/css/main.css'],
  icon: {
    clientBundle: {
      icons: ['lucide:heart', 'simple-icons:github']
    }
  }
});

Or enable clientBundle.scan to bundle every icon used across your app by scanning your source, so you don't have to list them one by one:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['pohon-ui'],
  css: ['~/assets/css/main.css'],
  icon: {
    clientBundle: {
      scan: true
    }
  }
});

Scanning only reads .vue, .jsx, .tsx, .md, .mdc, .mdx, .yml and .yaml files, and it only matches icon names written as literal strings. Icons defined in a .ts or .js file, like a list of navigation links, are missed, as are names built dynamically like i-lucide-${name}. Set globInclude to scan those files as well. It replaces the default list, so keep the extensions you still need:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['pohon-ui'],
  css: ['~/assets/css/main.css'],
  icon: {
    clientBundle: {
      scan: {
        globInclude: ['**/*.{vue,jsx,tsx,md,mdc,mdx,yml,yaml,ts,js}']
      }
    }
  }
});

Dynamic names can still be bundled when you know the whole set at build time, by listing them in clientBundle.icons. Names that only exist at runtime, like icons coming from an API, can't be bundled at all. They are loaded on demand from the /api/_nuxt_icon route when your app is server-rendered, with the Iconify API as a fallback, and from the Iconify API directly when you use nuxt generate or ssr: false.

Read more about this in the @nuxt/icon documentation.

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, ./app/assets/icons:

assets/icons
├── add.svg
└── remove.svg

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

export default defineNuxtConfig({
  modules: ['pohon-ui'],
  css: ['~/assets/css/main.css'],
  icon: {
    customCollections: [{
      prefix: 'custom',
      dir: './app/assets/icons'
    }]
  }
});

Then you can use the icons like this:

<template>
  <PIcon name="i-custom-add" />
</template>
Read more about this in the @nuxt/icon documentation.

Theme

You can change the default icons used by components in your app.config.ts:

app.config.ts
export default defineAppConfig({
  ui: {
    icons: {
      arrowDown: 'i-lucide-arrow-down',
      arrowLeft: 'i-lucide-arrow-left',
      arrowRight: 'i-lucide-arrow-right',
      arrowUp: 'i-lucide-arrow-up',
      caution: 'i-lucide-circle-alert',
      check: 'i-lucide-check',
      chevronDoubleLeft: 'i-lucide-chevrons-left',
      chevronDoubleRight: 'i-lucide-chevrons-right',
      chevronDown: 'i-lucide-chevron-down',
      chevronLeft: 'i-lucide-chevron-left',
      chevronRight: 'i-lucide-chevron-right',
      chevronUp: 'i-lucide-chevron-up',
      close: 'i-lucide-x',
      copy: 'i-lucide-copy',
      copyCheck: 'i-lucide-copy-check',
      dark: 'i-lucide-moon',
      drag: 'i-lucide-grip-vertical',
      ellipsis: 'i-lucide-ellipsis',
      error: 'i-lucide-circle-x',
      external: 'i-lucide-arrow-up-right',
      eye: 'i-lucide-eye',
      eyeOff: 'i-lucide-eye-off',
      file: 'i-lucide-file',
      folder: 'i-lucide-folder',
      folderOpen: 'i-lucide-folder-open',
      hash: 'i-lucide-hash',
      info: 'i-lucide-info',
      light: 'i-lucide-sun',
      loading: 'i-lucide-loader-circle',
      menu: 'i-lucide-menu',
      minus: 'i-lucide-minus',
      panelClose: 'i-lucide-panel-left-close',
      panelOpen: 'i-lucide-panel-left-open',
      plus: 'i-lucide-plus',
      reload: 'i-lucide-rotate-ccw',
      search: 'i-lucide-search',
      stop: 'i-lucide-square',
      star: 'i-lucide-star',
      success: 'i-lucide-circle-check',
      system: 'i-lucide-monitor',
      tip: 'i-lucide-lightbulb',
      upload: 'i-lucide-upload',
      warning: 'i-lucide-triangle-alert'
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        icons: {
          arrowDown: 'i-lucide-arrow-down',
          arrowLeft: 'i-lucide-arrow-left',
          arrowRight: 'i-lucide-arrow-right',
          arrowUp: 'i-lucide-arrow-up',
          caution: 'i-lucide-circle-alert',
          check: 'i-lucide-check',
          chevronDoubleLeft: 'i-lucide-chevrons-left',
          chevronDoubleRight: 'i-lucide-chevrons-right',
          chevronDown: 'i-lucide-chevron-down',
          chevronLeft: 'i-lucide-chevron-left',
          chevronRight: 'i-lucide-chevron-right',
          chevronUp: 'i-lucide-chevron-up',
          close: 'i-lucide-x',
          copy: 'i-lucide-copy',
          copyCheck: 'i-lucide-copy-check',
          dark: 'i-lucide-moon',
          drag: 'i-lucide-grip-vertical',
          ellipsis: 'i-lucide-ellipsis',
          error: 'i-lucide-circle-x',
          external: 'i-lucide-arrow-up-right',
          eye: 'i-lucide-eye',
          eyeOff: 'i-lucide-eye-off',
          file: 'i-lucide-file',
          folder: 'i-lucide-folder',
          folderOpen: 'i-lucide-folder-open',
          hash: 'i-lucide-hash',
          info: 'i-lucide-info',
          light: 'i-lucide-sun',
          loading: 'i-lucide-loader-circle',
          menu: 'i-lucide-menu',
          minus: 'i-lucide-minus',
          panelClose: 'i-lucide-panel-left-close',
          panelOpen: 'i-lucide-panel-left-open',
          plus: 'i-lucide-plus',
          reload: 'i-lucide-rotate-ccw',
          search: 'i-lucide-search',
          stop: 'i-lucide-square',
          star: 'i-lucide-star',
          success: 'i-lucide-circle-check',
          system: 'i-lucide-monitor',
          tip: 'i-lucide-lightbulb',
          upload: 'i-lucide-upload',
          warning: 'i-lucide-triangle-alert'
        }
      }
    })
  ]
})
Icons you override in app.config.ts are embedded in the client bundle like Pohon UI's own, as long as their collection is installed and their names are written in the file. A name imported from another file or built dynamically is loaded on demand at runtime. Add it to @nuxt/icon's clientBundle.icons to bundle it.