← 返回 Skill 列表
extension
分类: 开发与工程API Key 暂未确认

denji

使用Denji CLI将SVG图标作为框架组件进行管理。当用户需要在React、Preact、Solid、Qwik、Vue或Svelte项目中添加、删除、列出或管理SVG图标时使用。触发条件包括“添加图标”、“设置图标”、“管理SVG图标”、“删除图标”、“列出图标”,或任何涉及将Iconify图标作为框架组件的任务。

person作者: jakexiaohubgithub

Managing SVG Icons with Denji

Denji converts Iconify SVG icons into typed framework components. Icons are fetched, optimized (SVGO), and generated as native components for your framework.

Core Workflow

# 1. Initialize project
npx denji init --framework react --output ./src/icons.tsx

# 2. Add icons (prefix:name format from Iconify)
npx denji add lucide:check lucide:x lucide:arrow-right

# 3. Use in your code
import { Icons } from "./icons";

<Icons.Check className="size-4 text-green-500" />
<Icons.X className="size-4 text-red-500" />

Commands

denji init

Initialize a Denji project. Creates denji.json config and icons template.

npx denji init
npx denji init --framework react --output ./src/icons.tsx
npx denji init --framework svelte --output ./src/icons --output-type folder
npx denji init --framework react --no-typescript --output ./src/icons.jsx
npx denji init --a11y hidden --forward-ref

| Flag | Description | |------|-------------| | --framework <name> | react, preact, solid, qwik, vue, svelte | | --output <path> | Output path for icons file/folder | | --output-type <type> | file (single file) or folder (one file per icon) | | --typescript / --no-typescript | TypeScript or JavaScript (default: TS) | | --a11y <strategy> | hidden, img, title, presentation, or false (no a11y attrs) | | --forward-ref / --no-forward-ref | Use forwardRef (React/Preact only) | | --track-source / --no-track-source | Track Iconify source via data-icon attr | | -c, --cwd <path> | Working directory |

Missing flags trigger interactive prompts.

denji add <icons...>

Add icons from Iconify. Icons use prefix:name format.

npx denji add lucide:check
npx denji add lucide:check mdi:home radix-icons:cross-2
npx denji add lucide:star --name FavoriteStar
npx denji add lucide:info --a11y img
# Preview without writing files
npx denji add lucide:check mdi:home --dry-run

| Flag | Description | |------|-------------| | --name <name> | Custom component name (single icon only) | | --a11y <strategy> | Override a11y strategy for this icon | | --dry-run | Preview what would be generated without writing any files | | -c, --cwd <path> | Working directory |

Icon naming: lucide:arrow-right becomes ArrowRight (PascalCase). Override with --name.

Adding an existing icon updates it in place.

--dry-run skips file writes and hooks but still validates icon names, allowedLibraries, and config. Useful for CI checks and PR previews.

◇ denji add
│
○ [dry-run] Would add Check → ./src/icons.tsx
○ [dry-run] Would add Home → ./src/icons.tsx
│
◇ Dry run complete — 2 icon(s) previewed, no files written

denji remove <icons...>

Remove icons by component name. Aliases: rm, delete, del.

npx denji remove Check
npx denji rm Check Home ArrowRight

| Flag | Description | |------|-------------| | -c, --cwd <path> | Working directory |

denji list

List all icons in your project.

npx denji list
npx denji list --display json
npx denji list --display toon

| Flag | Description | |------|-------------| | --display <mode> | Output mode: default (human-readable), json, or toon | | -c, --cwd <path> | Working directory |

Shows component names and Iconify source (if trackSource: true).

Default output:

Found 3 icon(s) in ./src/icons.tsx

Icons:
  • Check (lucide:check)
  • HomeOutline (mdi:home-outline)
  • ArrowRight (lucide:arrow-right)

JSON output (--display json):

{
  "count": 3,
  "output": "./src/icons.tsx",
  "icons": [
    { "name": "Check", "source": "lucide:check" },
    { "name": "HomeOutline", "source": "mdi:home-outline" },
    { "name": "ArrowRight", "source": "lucide:arrow-right" }
  ]
}

TOON output (--display toon) uses TOON format for machine-readable binary encoding.

denji export

Export a JSON manifest of all tracked icons.

npx denji export                    # print to stdout
npx denji export --output icons.json
npx denji export --output           # writes to denji-export.json

| Flag | Description | |------|-------------| | --output [path] | Write to file (default: denji-export.json if no path given) | | -c, --cwd <path> | Working directory |

Output format:

{
  "version": 1,
  "framework": "react",
  "output": "./src/icons.tsx",
  "icons": [
    { "name": "Home", "source": "mdi:home" },
    { "name": "Check", "source": "lucide:check" }
  ]
}

source is included only when trackSource: true (the default).

denji import

Bulk-add icons from a manifest JSON file, a plain text file, or stdin.

npx denji import icons.json         # from denji export manifest
npx denji import icons.txt          # one prefix:name per line
echo "mdi:home\nlucide:check" | npx denji import   # from stdin
npx denji import icons.json --dry-run

| Flag | Description | |------|-------------| | --dry-run | Preview without writing files | | -c, --cwd <path> | Working directory |

Icons without prefix:name format are skipped with a warning. JSON manifest entries without a source field are also skipped.

denji clear

Remove all icons. Aliases: clr, reset.

npx denji clear
npx denji clear --yes

| Flag | Description | |------|-------------| | -y, --yes | Skip confirmation prompt | | -c, --cwd <path> | Working directory |

Config (denji.json)

The $schema field depends on how Denji is installed:

  • Locally installed (npm i -D denji): "./node_modules/denji/configuration_schema.json"
  • Not installed (using npx, bunx, pnpx, yarn dlx): "https://denji-docs.vercel.app/configuration_schema.json"
{
  "$schema": "./node_modules/denji/configuration_schema.json",
  "framework": "react",
  "output": "./src/icons.tsx",
  "typescript": true,
  "a11y": "hidden",
  "trackSource": true,
  "allowedLibraries": ["lucide"],
  "react": {
    "forwardRef": true
  },
  "hooks": {
    "postAdd": ["npx biome check --write ./src/icons.tsx"],
    "postRemove": ["npx biome check --write ./src/icons.tsx"]
  }
}

Key fields:

| Field | Type | Description | |-------|------|-------------| | framework | string | Required. react, preact, solid, qwik, vue, svelte | | output | string or object | Required. Path string or { type: "file"\|"folder", path: "..." } | | typescript | boolean | Default: true | | a11y | string or false | hidden, img, title, presentation, false | | trackSource | boolean | Default: true. Adds data-icon attr | | allowedLibraries | string[] | Restrict to specific Iconify prefixes. Empty/omitted = all allowed | | hooks | object | Lifecycle hooks (see below) |

Output Modes

File mode (default for React, Preact, Solid, Qwik, Vue): All icons in one file.

{ "output": "./src/icons.tsx" }

Folder mode (required for Svelte, optional for others): One file per icon + barrel export.

{ "output": { "type": "folder", "path": "./src/icons" } }

Hooks

Run shell commands at lifecycle points:

{
  "hooks": {
    "preAdd": ["echo 'Adding icons...'"],
    "postAdd": ["npx prettier --write ./src/icons.tsx"],
    "preRemove": [],
    "postRemove": [],
    "preClear": [],
    "postClear": [],
    "preList": [],
    "postList": []
  }
}

Common Patterns

Dynamic Icons

import { Icons, type IconName, type IconProps } from "./icons";

function DynamicIcon({ name, ...props }: { name: IconName } & IconProps) {
  const Icon = Icons[name];
  return <Icon {...props} />;
}

<DynamicIcon name="Check" className="size-4" />

Accessibility

// Decorative icon (hidden from screen readers)
<button>
  <Icons.Check aria-hidden="true" />
  Save
</button>

// Semantic icon (announced by screen readers)
<Icons.Check role="img" aria-label="Success" />

// Icon-only button
<button aria-label="Close">
  <Icons.X aria-hidden="true" />
</button>

Restricting Icon Sources

{
  "allowedLibraries": ["lucide"]
}
npx denji add lucide:check   # ✓ allowed
npx denji add mdi:home       # ✗ Error: Icon "mdi:home" is not allowed. Allowed libraries: lucide

Formatting with Hooks

{
  "hooks": {
    "postAdd": ["npx biome check --write ./src/icons.tsx"],
    "postRemove": ["npx biome check --write ./src/icons.tsx"]
  }
}

Using forwardRef (React/Preact)

{
  "framework": "react",
  "react": { "forwardRef": true }
}
const ref = useRef<SVGSVGElement>(null);
<Icons.Check ref={ref} className="size-4" />

Framework Quick Reference

| Framework | Extensions | Default Output | Config Key | Notes | |-----------|-----------|----------------|------------|-------| | React | .tsx/.jsx | file | react | forwardRef option | | Preact | .tsx/.jsx | file | preact | forwardRef option (via preact/compat) | | Solid | .tsx/.jsx | file | solid | Refs work natively as props | | Qwik | .tsx/.jsx | file | qwik | Uses component$() in folder mode | | Vue | .ts/.js | file | vue | Uses h() render functions | | Svelte | .svelte | folder (only) | svelte | Svelte 5 $props() runes |

Deep-Dive References

| Reference | Content | |-----------|---------| | references/configuration.md | Full config schema, all framework options, output normalization | | references/framework-patterns.md | Per-framework code examples, file vs folder imports, TypeScript types |