@cdx-ui/icons

中文文档
0.3.12发布于 2026/11/04TypeScriptESM + CJS零依赖近 30 天下载 3107

coverage

原始使用文档
文档目录19 节
  1. Installation
  2. Peer dependencies
  3. Usage
  4. Props
  5. Factory and types
  6. Naming conventions
  7. Aliases
  8. Country flags
  9. Accessibility
  10. Custom icons
  11. Brand logos
  12. Development
  13. Downloading Material Symbols SVGs
  14. Generating icon components
  15. Generating bottom-tab icons
  16. Tab icon registry
  17. Building
  18. Package structure
  19. Architecture

@cdx-ui/icons

coverage

Cross-platform icon library for Forge UI, built on Material Symbols (outlined style, weight 400, optical size 24). Renders via react-native-svg and targets iOS, Android, and Web from a single codebase.

The full Material Symbols outlined set is available, with outlined and filled variants for each icon, plus support for custom SVGs. Browse the complete library in the icon browser.

Installation

pnpm add @cdx-ui/icons

Peer dependencies

PackageVersion
react^18.2.0 || ^19.0.0
react-native>=0.76.0 (optional)
react-native-svg^13.0.0 || ^14.0.0 || ^15.0.0

Usage

import { Home, Favorite, Settings } from '@cdx-ui/icons';

// Outlined (default)
<Home size={24} color="blue" />

// Filled variant
<Favorite size={24} color="red" filled />

// Toggle between variants
<Home filled={isActive} />

Each icon is a forwardRef component that accepts all standard SvgProps plus the props below.

Props

PropTypeDefaultDescription
sizenumber | string24Sets both width and height.
colorstring'currentColor'Fill color applied to the icon.
filledbooleanfalseRenders the filled variant when true. Falls back to outlined if no filled data exists.
accessibilityLabelstring—When provided, the icon becomes informative (accessible={true}, accessibilityRole="image"). When omitted, the icon is decorative by default.
data-testidstring—Forwarded to the root <Svg> element for testing.

All other props are spread onto the root <Svg> element, including standard React Native accessibility props.

Factory and types

ExportDescription
createForgeIconFactory function to create custom Forge-style icon components from SVG data
ForgeIconPropsProps interface for icons created by the factory
ForgeIconType alias for a Forge icon component
IconNodeType for SVG path data consumed by the factory
IconDataType for outlined + filled path data pair

Naming conventions

Every icon is available under three export patterns:

PatternExampleUse case
PascalCaseHomePrimary import
SuffixedHomeIconAvoids collision with your own Home component
PrefixedCdxHomeNamespace when mixing multiple icon libraries
import { Home } from '@cdx-ui/icons';
import { HomeIcon } from '@cdx-ui/icons';
import { CdxHome } from '@cdx-ui/icons';

Aliases

Icons can have metadata-driven aliases defined in icon-metadata.json. Aliases let you reference the same underlying icon under alternative names — useful for legacy compatibility, domain-specific terminology, or aligning with names used by other icon sets. The generation script produces alias re-exports alongside the standard exports, so aliases are zero-cost at runtime.

Each entry in icon-metadata.json maps a base icon name (matching its SVG filename) to an array of aliases. An alias can be a plain string or an object with deprecated: true and a deprecationReason to flag names that should be migrated away from.

// icon-metadata.json
{
  "home": {
    "aliases": [
      "house",
      {
        "name": "old-home-icon",
        "deprecated": true,
        "deprecationReason": "Renamed to Home",
      },
    ],
  },
  "settings": {
    "aliases": ["gear", "preferences"],
  },
}

The config above produces the following behavior:

  • House, HouseIcon, and CdxHouse all resolve to the Home icon
  • Gear and Preferences resolve to the Settings icon
  • OldHomeIcon resolves to Home but shows a @deprecated Renamed to Home JSDoc warning in your editor, nudging consumers toward the canonical name
import { House } from '@cdx-ui/icons'; // alias → Home
import { Gear } from '@cdx-ui/icons'; // alias → Settings
import { OldHomeIcon } from '@cdx-ui/icons'; // deprecated alias → Home (editor warning)

Aliases that collide with an existing base icon name are silently skipped during generation to avoid duplicate exports.

Country flags

Country flags are exported from the @cdx-ui/icons/flags subpath, not from the main @cdx-ui/icons barrel. Each flag is a ForgeIcon. An alpha-2 code keeps its letters in the component name, such as FlagUS or FlagXK. A subdivision code replaces the hyphen with an underscore in the component name (FlagGB_ENG for GB-ENG). The subpath also exports a flags record keyed by the code, hyphen included, and a FlagCode union. FlagCode matches the country-flag-icons 3x2 set this package was built with, including the unofficial codes that package ships for libphonenumber-js.

Each flag path has an explicit fill, so the artwork keeps its source colors. Text color classes and theme tokens do not recolor it.

CountryFlag from @cdx-ui/components is the component for product UI, including the leading slot of a country select. It resolves artwork through the flags record, so rendering CountryFlag bundles every flag. An app that shows one fixed flag can import that icon instead:

import { FlagUS, flags, type FlagCode } from '@cdx-ui/icons/flags';

<FlagUS />;

const Flag = flags.US;

Accessibility

Icons are decorative by default — hidden from assistive technology when no accessibilityLabel is provided. This is the correct behavior for icons next to text labels or inside labeled containers.

// Decorative (default) — screen readers skip this
<Home size={20} color={theme.colors.icon} />

// Informative — screen readers announce "Home, image"
<Home size={20} accessibilityLabel="Home" />
ScenarioWhat to do
Icon next to a text labelLeave as default (decorative)
Icon inside a labeled ButtonLeave as default — the button's label covers it
Standalone icon conveying meaningAdd accessibilityLabel describing the icon's purpose in context
Icon-only buttonLabel the Pressable wrapper, not the icon

Cross-platform behavior:

  • iOS (VoiceOver): Announced as "{label}, image"
  • Android (TalkBack): Announced as "{label}, graphic"
  • Web (React Native Web): Renders role="img" + aria-label

React Native >=0.76 aliases aria-label to accessibilityLabel, so either prop works.

For full details, see the icon accessibility guidance on the docs site.

Custom icons

Place custom SVGs in icons/custom/. The generation script reads from both icons/material/outlined/ and icons/custom/, so custom icons are treated identically to Material Symbols icons.

  • Use {name}.svg for the outlined variant
  • Optionally add {name}-fill.svg for a filled variant
  • Author the path in the 0 -960 960 960 coordinate space. createForgeIcon forces viewBox="0 -960 960 960" on every icon, and the generator keeps only the root's child elements — it discards each SVG's own viewBox/width/height. A standard 24px export (path coordinates in 0..24) is not compatible: it renders as a tiny speck outside the viewBox (effectively invisible).
  • Drawable elements must be direct children of <svg>. The parser is one level deep, so a <path> nested inside a <g> is dropped. Flatten any group (and bake its transform into the path d) before committing.

To convert a 24px brand/Figma export into the 960 grid, apply the affine transform x' = 40·x, y' = 40·y − 960 (uniform scale ×40, then translate y by −960) to the path data — e.g. with svgpath: svgpath(d).scale(40).translate(0, -960).round(2).toString(). See icons/custom/whats_app.svg for a normalized reference. Note that icons render monochrome in currentColor; any source fill is overridden by the color prop.

Brand logos

Brand logos (WhatsApp, Google, GitHub, …) are named Logo<Brand> in the Figma Icon Library, but are published without the prefix and with brand casing — e.g. Figma LogoWhatsApp → export WhatsApp. Name the SVG with the de-prefixed, snake_cased filename so the generator produces the right export:

Figma componentSVG filenameExport
LogoWhatsAppwhats_app.svgWhatsApp
LogoYouTubeyou_tube.svgYouTube
LogoGitHubgit_hub.svgGitHub
LogoLinkedInlinked_in.svgLinkedIn

The Code Connect batch script (repo-root scripts/generate-icon-batch.ts) strips the Logo prefix when cross-referencing Figma components against local files, so the mapping resolves automatically. Material Symbols that genuinely start with Logo (e.g. logo_dev) match by exact name first and are unaffected.

Development

Downloading Material Symbols SVGs

SVGs are fetched directly from the Google Fonts CDN by a custom download script (scripts/download-icons.ts). The script queries Google's metadata API for the active icon list, then downloads only the outlined style (weight 400, opsz 24) with both fill states. Downloaded SVGs are not checked into git.

pnpm --filter @cdx-ui/icons download-icons

This downloads an outlined and a filled SVG for every icon to icons/material/outlined/. Pass --clean to re-download from scratch instead of skipping existing files.

Generating icon components

The generation script (scripts/generate-icons.ts) reads SVGs from both icons/material/outlined/ and icons/custom/, parses them with svgson, and writes TypeScript files to src/icons/ and src/aliases/.

pnpm --filter @cdx-ui/icons generate

Generated files include:

  • src/icons/{name}.ts — one file per icon with both outlined and filled path data
  • src/icons/index.ts — barrel export of all icons
  • src/aliases/aliases.ts — metadata-driven legacy name mappings
  • src/aliases/suffixed.ts — {Name}Icon exports for every icon and alias
  • src/aliases/prefixed.ts — Cdx{Name} exports for every icon and alias

Each generated icon file includes a base64-encoded @preview JSDoc tag, so hovering over an icon import in your editor shows a visual preview.

Generating bottom-tab icons

Native bottom tabs need image assets, not React components. A second generation script (scripts/generate-tab-icons.ts) rasterizes the curated subset declared in tab-icons.config.json into monochrome template PNGs (24/48/72 px → @1x/@2x/@3x) using @resvg/resvg-js, and emits platform-split descriptor modules consumed via the @cdx-ui/icons/tab-icons subpath export.

pnpm --filter @cdx-ui/icons generate:tab-icons

Generated files (gitignored, like src/icons/):

  • src/tab-icons/assets/{name}[-fill][@2x|@3x].png — template PNGs (black + alpha; the tab bar tints them at runtime)
  • src/tab-icons/generated/{name}.ts — web descriptor (SVG component only, no asset imports)
  • src/tab-icons/generated/{name}.native.ts — native descriptor with static require() asset imports
  • src/tab-icons/generated/registry.ts — TAB_ICON_NAMES tuple, TabIconName type, and TAB_ICON_COMPONENTS map
  • src/tab-icons/generated/index.ts — barrel exporting descriptors + registry

To add an icon to the curated set, add its base name to tab-icons.config.json and rebuild — unknown names fail the build. Consumer-facing usage is documented in the repository's icon guide.

Tab icon registry

The generation script also emits a registry module that provides programmatic access to the curated tab icon set. The registry exports are available from the @cdx-ui/icons/tab-icons subpath:

ExportTypeDescription
TAB_ICON_NAMESreadonly string[] (const tuple)Sorted array of all snake_case icon names from the config
TabIconNameunion typeString literal union derived from the tuple (e.g. 'home' | 'settings' | …)
TAB_ICON_COMPONENTSRecord<TabIconName, ForgeIcon>Maps each name to its SVG icon component
import { TAB_ICON_NAMES, TAB_ICON_COMPONENTS, type TabIconName } from '@cdx-ui/icons/tab-icons';

// Iterate available names
TAB_ICON_NAMES.forEach((name) => console.log(name));

// Look up a component by name
const Icon = TAB_ICON_COMPONENTS['home'];

// Type-safe name parameter
function getTabIcon(name: TabIconName) {
  return TAB_ICON_COMPONENTS[name];
}

Building

pnpm --filter @cdx-ui/icons build

This runs clean -> generate -> generate:tab-icons -> bob build. A prebuild hook runs download-icons first, so a clean checkout fetches the source SVGs automatically before generation. Output goes to lib/ with three targets:

  • lib/commonjs/ — CJS modules
  • lib/module/ — ESM modules
  • lib/typescript/ — per-file .d.ts declarations

The package is compiled by react-native-builder-bob, consistent with all other workspace packages. Unlike other workspace packages, the react-native field points to lib/module/index.js (the pre-built ESM output) rather than source. This means the icons package must be built (pnpm build:packages or pnpm --filter @cdx-ui/icons build) before Metro can resolve it. Source maps are excluded from the published package (!lib/**/*.map in files) to reduce install size.

Package structure

icons/
├── icons/
│   ├── material/outlined/     # Downloaded SVGs (not checked in)
│   └── custom/                # Hand-authored custom SVGs
├── scripts/
│   ├── download-icons.ts      # Downloads outlined SVGs from Google Fonts CDN
│   ├── generate-icons.ts      # SVG → TypeScript generation script
│   ├── generate-tab-icons.ts  # SVG → template PNG + descriptor generation
│   ├── generate-manifest.ts   # Emits icon-manifest.json (the ./manifest export)
│   └── shared.ts              # Naming utilities shared by both generators
├── src/
│   ├── icons/                 # Generated icon components
│   ├── aliases/               # Generated alias re-exports
│   ├── tab-icons/             # Bottom-tab descriptors (entry + generated assets)
│   ├── createForgeIcon.ts       # Factory function
│   ├── defaultAttributes.ts   # SVG defaults (viewBox, fill)
│   ├── types.ts               # TypeScript types
│   └── index.ts               # Main entry point
├── types/png.d.ts             # Ambient *.png module declaration (kept out of src/)
├── icon-metadata.json         # Alias and deprecation metadata
├── tab-icons.config.json      # Curated bottom-tab icon subset
├── package.json
├── tsconfig.json
└── tsconfig.build.json

src/icons/, src/aliases/, src/tab-icons/assets/, and src/tab-icons/generated/ contain generated files (gitignored except for .gitkeep sentinels).

Architecture

The package follows the patterns established by lucide-react-native, adapted for Material Symbols. Key differences:

  • Fill-based icons — Material Symbols icons use fill exclusively (no stroke attributes). The color prop maps to fill.
  • 960x960 coordinate system — viewBox="0 -960 960 960" instead of Lucide's 0 0 24 24. The size prop controls rendered dimensions; the viewBox handles internal coordinates.
  • Outlined/filled variants — Each icon bundles both variants via the filled prop, modeling Material Symbols' fill axis. This halves the export count vs. separate components.
  • Decorative by default — Icons are hidden from assistive technology unless accessibilityLabel is provided.
  • OTA compatibility — childDefaultAttributes explicitly sets fill on every child SVG element, working around an inheritance bug in CodePush/expo-updates.

The full research and design rationale is recorded in the repository's icon-package research spike.

版本与发布记录

@cdx-ui/icons 共发布 243 个版本,首次发布于 2026/03/12;过去 12 个月发布了 243 个版本。

版本发布日期距今
0.3.12latest2026/11/044 天前
0.3.112026/10/309 天前
0.3.102026/10/309 天前
0.3.92026/10/3010 天前
0.3.82026/10/2811 天前
0.3.72026/10/2713 天前

其他发布通道:staging → 0.1.0。

关于 @cdx-ui/icons 的常见问题

如何安装 @cdx-ui/icons?

在项目目录运行 npm install @cdx-ui/icons,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^18.2.0 || ^19.0.0)、react-native-svg(^13.0.0 || ^14.0.0 || ^15.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@cdx-ui/icons 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@cdx-ui/icons 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@cdx-ui/icons 可以商用吗?

@cdx-ui/icons 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@cdx-ui/icons 的下载量有多少?

根据 npm 官方下载统计,@cdx-ui/icons 在 2026/10/10 – 2026/11/08 这 30 天内被下载 3,107 次,日均约 104 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@cdx-ui/icons 还在维护吗?

最新版本 0.3.12 发布于 2026/11/04(4 天前);至今共发布 243 个版本,其中过去 12 个月发布了 243 个;首次发布于 2026/03/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。