Lucide only
Every icon in Kiwa UI comes from Lucide. One icon set means a consistent stroke, weight, and geometry across components. No mixing styles, no duplicate visuals.
Blocks and primitives must not contain inline SVG icon components. SVG is only allowed for non-icon visuals like charts, gradients, or decorative patterns.
The icon layer
The canonical file is registry/ui/icon.tsx. It exposes:
| Export | Description |
|---|---|
Icon | A component that takes an iconNode prop and renders a fixed 24×24 SVG. |
createIcon(iconNode) | A factory that turns any Lucide IconNode into a named component. |
| Semantic named exports | Around 190 of them, like ButtonIcon, CheckIcon, SearchIcon, ChevronDownIcon. Every name ends in Icon. |
Sizing is applied via class. The viewBox stays fixed at 24×24 so icons scale crisply from 12px to 48px without blur.
Usage
import { CheckIcon, SearchIcon } from '@/components/ui/icon'
export const Status = () => (
<div class='flex items-center gap-2'>
<CheckIcon class='size-4 text-success' />
<span>Saved</span>
</div>
)
export const SearchButton = () => (
<button class='flex items-center gap-2'>
<SearchIcon class='size-5' />
Search
</button>
)Adding a new icon
Lucide ships hundreds of icons. If the one you need isn't already exported from icon.tsx, add it with createIcon:
// registry/ui/icon.tsx
import { Sparkles } from 'lucide'
// …existing exports…
export const SparklesIcon = createIcon(Sparkles)Use a semantic name when one fits (e.g. DeleteIcon for Trash2) so callers don't have to know the underlying Lucide name.
When to use SVG directly
Reach for raw <svg> only for visuals that aren't icons: chart primitives, gradient backgrounds, decorative patterns, product screenshots rendered as vectors. If it represents an action or object in the interface, it's an icon and it must come from Lucide.