unocss-iconify-helper
v1.0.10
Published
Dev-only Iconify icon picker for UnoCSS. Framework-agnostic Vite plugin — works with Vue, React, Svelte, etc.
Maintainers
Readme
unocss-iconify-helper
专为 UnoCSS + Vite 项目设计的开发时图标选择器与预设增强工具。框架无关,支持 Vue、React、Svelte、Vanilla 等一切 Vite 项目。
English | 中文
为什么会有这个项目?
在使用 UnoCSS + Iconify 开发时,有几个痛点:
1. 图标难查找
Iconify 有数百个图标库、数十万个图标。每次想用一个图标都要打开 icones.js.org 搜索,找到后还要手动拼出 i-{prefix}-{name} 的 class 名称,来回切换很低效。
2. 图标库管理混乱,影响团队一致性和构建体积
随着项目迭代,不同开发者会引入不同的图标库——有人用 mdi、有人用 tabler、有人用 heroicons。没有任何工具告诉你当前项目实际用到了哪些 @iconify-json/* 包。图标库越多,开发时下载的 JSON 越大,风格也越不统一。最好的状态是:整个项目只用一两个图标库,保持视觉风格统一,同时最小化依赖。
3. pnpm workspace + Windows 下 autoInstall 静默失败(特定场景)
如果你在 Windows 上使用 pnpm workspace,@unocss/preset-icons 的 autoInstall: true 会因为内部 mlly 库拿到的是 Windows 路径(D:\...)而非合法的 file:// URL,导致图标包解析失败、图标无法显示。
这个包解决了上述问题:
- 快捷键(默认
Ctrl+I)唤出浮层图标选择器,搜索即用,点击自动复制 UnoCSS class - 自动扫描源码,识别项目已用到的图标库,在选择器中优先展示项目已用库,引导团队复用而非随意引入新库;并按图标库筛选搜索结果,减少干扰
- 重写了
presetIcons的集合加载机制,用createRequire+Proxy完全绕开mlly,在 pnpm workspace + Windows 下也能正常使用;首次遇到未安装的图标包时自动调用包管理器安装
安装
pnpm add -D unocss-iconify-helper使用
1. Vite 插件
// vite.config.ts
import { defineConfig } from 'vite'
import UnoCSS from 'unocss/vite'
import { iconifyHelperPlugin } from 'unocss-iconify-helper/vite'
export default defineConfig({
plugins: [
UnoCSS(),
iconifyHelperPlugin(), // 仅 dev 模式生效,production 自动禁用
],
})配置项(均为 helper 特有,非 Vite 原生配置):
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| hotkey | string | 'ctrl+i' | 唤出快捷键,格式如 'meta+shift+i' |
| extraCollections | Record<string, string> | {} | 额外图标库(名 → 说明),与扫描结果合并显示在面板 |
| scanDirs | string[] | ['src'] | 扫描源码的目录 |
| scanExts | string[] | ['.vue','.ts',...] | 扫描的文件后缀 |
| apiEndpoints | string[] | 见下方 | Iconify API 端点列表(详见下方说明) |
apiEndpoints 说明
弹窗头部有一个 API 节点切换按钮,用户可在运行时切换搜索来源,选择记录于 localStorage。
内置默认列表(三个节点服务内容完全一致,背后服务器不同,可在主节点不通时切换):
https://api.iconify.design ← 主节点(默认)
https://api.simplesvg.com ← 备用节点 1
https://api.unisvg.com ← 备用节点 2传入 apiEndpoints 会完全覆盖内置列表(不合并),数组第一项即默认节点:
iconifyHelperPlugin({
// 使用私有部署的 Iconify API,移除官方节点
apiEndpoints: ['https://iconify.your-company.com'],
})2. UnoCSS 预设
// uno.config.ts
import { defineConfig, presetUno } from 'unocss'
import { presetIconifyHelper } from 'unocss-iconify-helper/unocss'
export default defineConfig({
presets: [
presetUno(),
presetIconifyHelper(),
// 可覆盖任意选项:
// presetIconifyHelper({ autoInstall: false, scale: 1, warn: true }),
],
})presetIconifyHelper 接受所有 @unocss/preset-icons 原生选项(直接透传),同时对部分选项提供了增强行为或不同的默认值:
Helper 增强项(与原生 presetIcons 行为有差异):
| 参数 | 类型 | 默认值 | 与原生的差异 |
|------|------|--------|------------|
| autoInstall | boolean \| fn | true | 由 createRequire + Proxy 实现,绕开 mlly;在 pnpm workspace + Windows 下也能正常运行 |
| collections | Record<string, ...> | 自动检测 | 启动时自动扫描 node_modules/@iconify-json/*,无需手动配置;用户传入的值优先级更高 |
覆盖了原生默认值的项:
| 参数 | 原生默认值 | 本包默认值 | 说明 |
|------|-----------|-----------|------|
| scale | 1 | 1.2 | 图标略微放大,在正文中显示更清晰 |
| extraProperties | {} | {display:'inline-block','vertical-align':'middle'} | 图标默认内联对齐 |
其余透传项(与原生完全一致,直接参考 presetIcons 文档):
mode、prefix、warn、unit、layer、processor、customizations、iconifyCollectionsNames 等,用法与 presetIcons 完全相同。
无需提前安装 @iconify-json/*。 第一次遇到新图标库时自动安装,并写入 package.json,后续启动直接从本地加载。
工作原理
图标选择器(Vite 插件)
Vite dev server
├── configureServer → GET /__iph_client
│ window.__IHP_CONFIG__ = { hotkey, collections } ← 运行时配置
│ + dist/client.min.js ← 纯 JS 弹窗(IIFE,无框架依赖)
│
├── transformIndexHtml → 注入 <script src="/__iph_client" defer>
│
└── handleHotUpdate → server.ws.send('iph:collections', {...})
↑ 客户端通过 WebSocket 监听,无刷新更新筛选列表客户端脚本(src/client/index.ts)由 esbuild 打包为自包含 IIFE,无任何运行时依赖。调用 Iconify API 搜索图标,点击后写入剪贴板。
自动加载预设(presetIconifyHelper)
标准 presetIcons 在 Windows + pnpm workspace 下依赖 mlly 解析 @iconify-json/* 路径,而 mlly 收到的是 D:\... 格式的路径而非合法的 file:// URL,导致静默失败。
presetIconifyHelper 通过两层机制解决:
第一层 — 预扫描(快速路径,零网络请求):
启动时扫描 node_modules/@iconify-json/,用 Node 原生 createRequire 直接加载,构造 collections 映射后传给 presetIcons,完全绕开 mlly。
第二层 — Proxy 动态加载(按需安装路径):
collections 被封装为 JavaScript Proxy,拦截所有 prefix 访问。遇到未知前缀(如 i-phosphor-star 中的 phosphor)时:
Proxy.get('phosphor')
↓
createRequire('@iconify-json/phosphor') → 找不到
↓
向上遍历目录树检测包管理器(pnpm-lock.yaml / yarn.lock / bun.lockb)
↓
execSync('pnpm add -D @iconify-json/phosphor', { cwd, shell })
↓
createRequire('@iconify-json/phosphor') → 成功 → 返回 IconifyJSON同一 prefix 的并发请求共享同一个 Promise,确保只安装一次。
局限性
- 仅限开发时:
iconifyHelperPlugin在 production 构建中自动禁用;presetIconifyHelper是构建时安全的 - 需要 Node.js 环境:自动安装依赖
child_process.execSync,无 Node 环境(如纯浏览器/SSR)应传入autoInstall: false - 首次安装有延迟:安装新图标包时会阻塞事件循环几秒;后续启动瞬间完成
- 图标搜索需要网络:弹窗搜索调用
https://api.iconify.design,开发时需要能访问外网
License
MIT
unocss-iconify-helper (English)
A dev-only icon picker and preset enhancer for UnoCSS + Vite projects. Framework-agnostic — works with Vue, React, Svelte, Vanilla, and any Vite-based project.
Why does this exist?
Three pain points when working with UnoCSS + Iconify:
1. Hard to find icons
Iconify has hundreds of collections and hundreds of thousands of icons. Every time you need one, you open icones.js.org, search, and manually type out i-{prefix}-{name}. Slow and error-prone.
2. Scattered icon collections hurt consistency and build size
As a project grows, different developers pull in different icon libraries — one uses mdi, another tabler, another heroicons. No tool tells you which @iconify-json/* packages are actually in use. The more collections, the larger the dev downloads, and the less visually consistent the UI. The ideal: the whole project uses one or two icon libraries, keeping the style unified and dependencies minimal.
3. autoInstall silently fails on Windows + pnpm workspaces (specific scenario)
On Windows with pnpm workspace, @unocss/preset-icons's autoInstall: true silently fails — internally, mlly receives a raw Windows path (D:\...) instead of a proper file:// URL, so icon package resolution breaks.
This package addresses all three:
- A hotkey (
Ctrl+Iby default) opens a floating icon picker — search, click, UnoCSS class copied - Auto-scans source files to detect used icon collections; shows them first in the picker to guide team members toward reusing existing libraries rather than pulling in new ones; supports filtering by collection
- Reimplements the collection loading mechanism using
createRequire+Proxy, bypassingmllyentirely; works on Windows + pnpm workspaces; auto-installs missing packages on first use
Install
pnpm add -D unocss-iconify-helperUsage
Vite Plugin
// vite.config.ts
import { defineConfig } from 'vite'
import UnoCSS from 'unocss/vite'
import { iconifyHelperPlugin } from 'unocss-iconify-helper/vite'
export default defineConfig({
plugins: [
UnoCSS(),
iconifyHelperPlugin(), // auto-disabled in production
],
})| Option | Type | Default | Description |
|--------|------|---------|-------------|
| hotkey | string | 'ctrl+i' | Trigger hotkey, e.g. 'meta+shift+i' |
| extraCollections | Record<string, string> | {} | Extra collections to always show in the picker |
| scanDirs | string[] | ['src'] | Directories to scan for used icon classes |
| scanExts | string[] | ['.vue','.ts',...] | File extensions to scan |
| apiEndpoints | string[] | 3 official endpoints | Ordered list of Iconify API endpoints. First item is the default. Providing this list replaces (not merges with) the built-in list. A switcher button in the picker header lets users cycle through them; the choice is persisted to localStorage. |
Built-in default list (all serve identical data, different servers):
https://api.iconify.design ← default
https://api.simplesvg.com
https://api.unisvg.comUnoCSS Preset
// uno.config.ts
import { defineConfig, presetUno } from 'unocss'
import { presetIconifyHelper } from 'unocss-iconify-helper/unocss'
export default defineConfig({
presets: [
presetUno(),
presetIconifyHelper(),
],
})Passes all options through to @unocss/preset-icons. Differences from native behavior:
Enhanced options:
| Option | Type | Default | Difference from native |
|--------|------|---------|----------------------|
| autoInstall | boolean \| fn | true | Implemented via createRequire + Proxy; bypasses mlly; works on Windows + pnpm |
| collections | Record<string, ...> | auto-detected | Auto-scans node_modules/@iconify-json/* at startup; user-provided values take precedence |
Different defaults:
| Option | Native default | This package | Note |
|--------|---------------|-------------|------|
| scale | 1 | 1.2 | Slightly larger for better inline readability |
| extraProperties | {} | {display:'inline-block','vertical-align':'middle'} | Sensible inline alignment |
All other presetIcons options (mode, prefix, warn, unit, layer, processor, customizations, etc.) work exactly as documented in the official presetIcons docs.
You do not need to pre-install @iconify-json/* packages. They are installed on first use and persisted to package.json.
How It Works
Icon picker (Vite plugin)
The plugin injects a <script src="/__iph_client"> tag. The endpoint serves a runtime config object (window.__IHP_CONFIG__) followed by the minified IIFE client bundle. The client calls the Iconify API for search and writes the selected class to the clipboard. Collection updates are pushed via Vite's WebSocket without a page reload.
Auto-install preset
At startup, presetIconifyHelper scans node_modules/@iconify-json/ with createRequire and builds a static collections map (Layer 1). The map is wrapped in a Proxy (Layer 2) that intercepts unknown prefix lookups, detects the package manager by walking up the directory tree for lock files, runs the install command, and loads the result via createRequire — never touching mlly.
Limitations
- Dev-only: the Vite plugin is disabled in production; the preset is build-time safe
- Requires Node.js:
autoInstalluseschild_process.execSync; passautoInstall: falsein non-Node environments - First-use latency: installing a new collection blocks the event loop for a few seconds; subsequent starts are instant
- Icon search requires internet: the picker calls
https://api.iconify.design
License
MIT
