npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tenon-naive-iconify-picker

v0.1.3

Published

Offline-first icon picker for Vue 3 + Naive UI, powered by Iconify. Browse bundled icon sets offline, inject your own local SVGs, or use any online Iconify icon.

Downloads

628

Readme

tenon-naive-iconify-picker

English · 简体中文

面向 Vue 3 + Naive UI 的离线优先图标选择器,基于 Iconify。 可注册任意多个图标库(每个库一个 Tab),零网络请求离线浏览,可注入你自己的 SVG,选中结果就是一个字符串 —— 非常适合"给这个菜单选个图标"这类字段。


特性

  • 🧭 多图标库 —— 可注册任意多个图标库(Lucide、Ant Design、Element Plus、Phosphor…),每个库一个 Tab,用户从自己熟悉的库里选。
  • 🔌 离线 —— 注册的库从本地数据渲染,永远不会请求 api.iconify.design
  • 📦 零配置 —— 开箱自带 Lucide。导入即用,想要更多库再加。
  • 🎨 主题自适应 —— 边框、文字、悬停、主色、圆角全部通过 useThemeVars() 跟随宿主 Naive UI 主题(明 / 暗 / 自定义主色),无需接任何 CSS 变量。
  • 🖼️ 本地 SVG —— 注册你项目里的 SVG,以 local:<名字> 的形式选择。
  • 🌐 在线兜底 —— 输入任意 Iconify 名称(如 mdi:home),联网时在线加载。
  • 🌍 可国际化 —— 所有文案来自一个 labels prop。默认英文;传入你自己的翻译即可(见 国际化)。
  • 🧩 单字符串值 —— v-model 就是一个 prefix:name(或 local:name)字符串。直接存进数据库字段,任何地方用 <OfflineIcon> 渲染。

前置要求

这是一个用在既有应用里的组件 —— 它不打包 Vue 和 Naive UI,需要你的应用以 peer 依赖提供:

| Peer 依赖 | 支持范围 | | --- | --- | | vue | ^3.3.0 | | naive-ui | ^2.34.0 | | @iconify/vue | ^4.0.0 \|\| ^5.0.0 |

仅限浏览器(用到 navigator.onLinev-html、Iconify 的 addCollection)。SSR / Nuxt 下请放在仅客户端边界内渲染 —— 见 SSR / Nuxt

安装

npm i tenon-naive-iconify-picker
# pnpm add tenon-naive-iconify-picker
# yarn add tenon-naive-iconify-picker

样式由组件自动注入,无需单独 import 任何 CSS。

快速开始

零配置:导入即用,内置的 Lucide 库已自动注册。

<script setup lang="ts">
import { ref } from 'vue'
import { IconPicker, OfflineIcon } from 'tenon-naive-iconify-picker'

const icon = ref('lucide:rocket')
</script>

<template>
  <!-- 必须在应用的 <n-config-provider> 内,主题才能解析 -->
  <IconPicker v-model="icon" />

  <!-- 任意位置渲染已存的值 —— 同样离线,也支持 local: -->
  <OfflineIcon :icon="icon" :size="18" />
</template>

v-model 保存的是单个字符串,如 lucide:rocketlocal:star。整个契约就这一点。

图标库(Tab)

你注册的每个图标库都会成为选择器里的一个 Tab,用户可以在不同库之间切换 —— 一个熟悉、较小的库往往比一整个大集更好找。图标数据不打包进本组件:每个库在你的构建里是一个懒加载 chunk,只在第一次点开该 Tab 时加载,用多少付多少(Lucide ≈ 85 KB gz;Phosphor ≈ 946 KB gz)。

# 装你想要的库的数据包
npm i @iconify-json/ant-design @iconify-json/ep @iconify-json/ph
// main.ts
import { setupIconPicker, lucideCollection } from 'tenon-naive-iconify-picker'

setupIconPicker({
  collections: [
    lucideCollection, // 本包内置
    { prefix: 'ant-design', name: 'Ant Design',   loader: () => import('@iconify-json/ant-design/icons.json').then(m => m.default) },
    { prefix: 'ep',         name: 'Element Plus',  loader: () => import('@iconify-json/ep/icons.json').then(m => m.default) },
    { prefix: 'ph',         name: 'Phosphor',      loader: () => import('@iconify-json/ph/icons.json').then(m => m.default) },
  ],
})

保存的值带库前缀(ant-design:home-outlinedlucide:rocket),所以 <OfflineIcon> 总能正确渲染。你也可以用 <IconPicker :collections> prop 按实例传。到 icon-sets.iconify.design 浏览所有可用库;prefix 必须和库匹配。

配置

setupIconPicker(options) —— 可选的一次性配置

在应用启动时调用一次,用来注册图标库、注入本地 SVG、或预热某个库。全部可选;不调用就只有内置的 Lucide。

| 选项 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | collections | IconCollection[] | defaultCollections(Lucide) | 作为 Tab 显示的图标库,每个带懒加载 loader。 | | localIcons | Record<string, string> | — | 本地 SVG。接受 { 名字: 原始SVG }import.meta.glob 的结果。 | | preloadPrefix | string | 第一个库 | 启动时预热哪个库,让首次渲染即时呈现。 |

<IconPicker> props

| Prop | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | v-model | string | '' | 保存的值:prefix:namelocal:name。 | | collections | IconCollection[] | 全局 / Lucide | 仅覆盖本实例的图标库。 | | localIcons | Record<string, string> | — | { 名字: 原始SVG }import.meta.glob 结果。 | | labels | Partial<IconPickerLabels> | 英文 | 你的翻译 —— 见 国际化。 | | searchIcon / clearIcon | string | lucide:search / lucide:x | 搜索/清除图标;请用已注册库里的名字。 | | fallbackIcon | string | '' | 空值时 OfflineIcon 的兜底图标。 | | clearable | boolean | true | 是否显示清除(×)按钮。 | | cap | number | 300 | 单页最多渲染多少个图标,超出提示"继续输入以缩小范围"。 |

<OfflineIcon> props

独立的渲染器,用于展示已存的值 —— 表格、菜单、任何地方都行。

| Prop | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | icon | string | '' | prefix:namelocal:name。 | | size | number \| string | 18 | 数字按 px 处理。 | | fallback | string | '' | icon 为空时渲染它。 |

国际化 (i18n)

组件内部不含任何 i18n 框架,也不假设你用什么。所有可见文案都来自一个 labels 对象 —— 默认英文(defaultLabels)。通过 labels prop 覆盖任意子集;没传的键回退到英文。

文案键

| 键 | 英文默认 | | --- | --- | | placeholder | Select icon | | title | Select icon | | search | Search icon name… | | local | Local SVG | | online | Online | | onlinePlaceholder | Any Iconify name, e.g. mdi:home | | use | Use | | offlineHint | Offline — online icons that are not bundled cannot be previewed | | loading | Loading… | | empty | No matching icon | | more | {n} more — keep typing to narrow |

more 支持 {n} 占位,渲染时替换为被隐藏的图标数量。

静态覆盖

<IconPicker
  v-model="icon"
  :labels="{ placeholder: '选择一个图标', title: '图标' }"
/>

接入你自己的 i18n(以 vue-i18n 为例)

t() 出来的响应式翻译直接传进去:

<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { IconPicker } from 'tenon-naive-iconify-picker'

const { t } = useI18n()

// 切换语言时会重新计算。
const iconLabels = computed(() => ({
  placeholder: t('iconPicker.placeholder'),
  title: t('iconPicker.title'),
  search: t('iconPicker.search'),
  local: t('iconPicker.local'),
  online: t('iconPicker.online'),
  onlinePlaceholder: t('iconPicker.onlinePlaceholder'),
  use: t('iconPicker.use'),
  offlineHint: t('iconPicker.offlineHint'),
  loading: t('iconPicker.loading'),
  empty: t('iconPicker.empty'),
  more: t('iconPicker.more', { n: '{n}' }), // 保留 {n} 占位,交给组件填充
}))
</script>

<template>
  <IconPicker v-model="icon" :labels="iconLabels" />
</template>

你的中文语言包大致长这样:

iconPicker: {
  placeholder: '选择图标',
  title: '选择图标',
  search: '搜索图标名称…',
  local: '本地 SVG',
  online: '在线',
  onlinePlaceholder: '任意 Iconify 名称,如 mdi:home',
  use: '使用',
  offlineHint: '离线 — 未内置的在线图标无法预览',
  loading: '加载中…',
  empty: '没有匹配的图标',
  more: '还有 {n} 个 — 继续输入以缩小范围',
}

本地 SVG 图标

注册你自己的 SVG,让它们出现在 Local SVG Tab 下,并能以 local:<名字> 渲染。

Vite(推荐)—— 用 glob 把 SVG 目录读成原始字符串,文件名(去掉 .svg)就是图标名:

import { registerLocalIcons } from 'tenon-naive-iconify-picker'

registerLocalIcons(
  import.meta.glob('/src/assets/svg/*.svg', { query: '?raw', import: 'default', eager: true }),
)
// star.svg  ->  local:star

(setupIconPicker({ localIcons })<IconPicker :localIcons> prop 效果相同。)

非 Vite 打包器 —— import.meta.glob 是 Vite 专属。用你工具链暴露原始文件内容的方式构造出 { 名字: 原始SVG } 映射,再传给 registerLocalIcons。组件核心不依赖 Vite。

"离线"是什么意思

注册的库作为懒加载 chunk 从你应用自己的源提供 —— 不会从 Iconify CDN 拉取任何东西。Online Tab 里输入的、你没有注册的那些库的图标,联网时从 Iconify API 加载(离线则不可用)。这是唯一的在线路径,且按图标逐个手动触发。

SSR / Nuxt

选择器用到了仅浏览器的 API。SSR 下请仅在客户端渲染,例如 Nuxt 的 <client-only>,或用 import.meta.client 守卫的动态导入。

开发

npm install
npm run dev        # playground(Vite)—— 上面的截图就来自这里
npm run build      # dist/(ESM + d.ts,CSS 注入)
npm run typecheck  # vue-tsc

许可证

MIT