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 codeimport { 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 writtendenji 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: lucideFormatting 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 |