Icon
Composant d'affichage d'icônes et son script de mise en cache automatique depuis l'API Iconify.
Installation
npx shadcn@latest add @oward/iconIntroduction
Ce composant d'icônes a été conçu pour répondre à trois exigences principales : optimiser le build et la taille du bundle JavaScript en évitant les dépendances runtime d'icônes volumineuses (comme @iconify/json), offrir une DX simple en ne demandant uniquement l'identifiants Iconify (ex: lucide:alert-triangle), et automatiser la récupération des icônes manquantes pendant le développement.
Fonctionnalités
- Téléchargement automatique : Les icônes sont récupérées depuis l'API Iconify lors de leur détection dans le code
- Git Ready : Les icones sont stockées en local et prêtes à être commités
- Cache local optimisé : Stockage des icônes en fichiers JSON avec imports tree-shakeable, prêts à être commités dans Git
- Hot Module Replacement : Support HMR via le script
icon:watchpour un développement fluide - Dimensionnement adaptatif : Taille par défaut de
1emqui s'adapte au contexte typographique, avec support de toutes les unités CSS (px, em, rem, etc.)
Comparaison
| Fonctionnalité | @oward/icon | Monicon | unplugin-icons | @iconify/react |
|---|---|---|---|---|
| SSR | ✔︎ | ✔︎ | ✔︎ | ❌ |
| Offline | ✔︎ | ✔︎ | ✔︎ | ❌ |
| Git Ready | ✔︎ | ✔︎ | ❌ | ❌ |
| Config free | ✔︎ | ❌ | ❌ | ✔︎ |
| Package free | ✔︎ | ❌ | ❌ | ❌ |
Utilisation du composant
Utilisation basique
import { Icon } from "@/ui/icon";
<Icon name="lucide:home" />
<Icon name="mdi:account" className="size-6" />
<Icon name="heroicons:heart" style={{ fontSize: "2rem" }} />Taille des icônes
Par défaut, les icônes ont une taille de 1em, ce qui leur permet de s'adapter automatiquement à la taille du texte environnant. Vous pouvez personnaliser la taille via la prop size ou avec des classes CSS :
// Via la prop size (pixels ou unités CSS)
<Icon name="lucide:home" size={24} />
<Icon name="lucide:home" size="2rem" />
<Icon name="lucide:home" size="1.5em" />
// Via des classes CSS
<Icon name="lucide:home" className="size-6" />
<Icon name="lucide:home" className="text-2xl" />Couleur avec currentColor
Les icônes utilisent currentColor par défaut, ce qui signifie qu'elles héritent de la couleur du texte de leur élément parent :
// L'icône prend la couleur du texte
<p className="text-blue-500">
<Icon name="lucide:info" /> Message d'information
</p>
// Couleur personnalisée via className
<Icon name="lucide:heart" className="text-red-500" />Personnalisation du stroke-width
Pour les icônes avec des strokes (comme Lucide, Tabler, etc.), vous pouvez personnaliser l'épaisseur du trait via la prop strokeWidth ou des variables CSS. Le système utilise une cascade de fallback :
strokeWidthprop /--icon-sw-override: Override d'instance (priorité maximale)--{collection}-sw: Override spécifique à la collection (ex:--lucide-sw)--icon-sw: Override global pour toutes les icônes- Valeur originale du SVG comme fallback final
// Via la prop strokeWidth (pour une instance spécifique)
<Icon name="lucide:home" strokeWidth={1} />
<Icon name="lucide:home" strokeWidth={3} />/* Configuration globale dans votre CSS */
:root {
--icon-sw: 1.5; /* Toutes les icônes -> Non recommandé */
--lucide-sw: 2; /* Uniquement Lucide */
--tabler-sw: 1.5; /* Uniquement Tabler */
}Cette fonctionnalité ne s'applique qu'aux icônes qui utilisent stroke-width
dans leur SVG (comme Lucide, Tabler, Heroicons outline, etc.). Les icônes
remplies (filled) ne sont pas affectées.
Attention avec asChild
Utilisation avec asChild
Lorsqu'il est utilisé avec asChild (comme dans TooltipTrigger, Button, etc.), cela peut causer des erreurs d'hydratation SSR car le clonage de l'élément serveur peut créer des incohérences entre le rendu serveur et client.
// ❌ À éviter - cause des erreurs d'hydratation
<TooltipTrigger asChild>
<Icon name="mdi:home" />
</TooltipTrigger>
// ✅ Correct - pas d'erreur d'hydratation
<TooltipTrigger>
<Icon name="mdi:home" />
</TooltipTrigger>
// ✅ Ou envelopper dans un élément HTML
<TooltipTrigger asChild>
<button>
<Icon name="mdi:home" />
</button>
</TooltipTrigger>Configuration du composant
Alias de path
Le composant a besoin d'un path alias pour accéder aux icônes mises en cache.
Ajoutez le path alias dans votre tsconfig.json ou jsconfig.json :
{
"compilerOptions": {
"paths": {
"#icons": ["./_icons"]
}
}
}Important
L'alias #icons pointe vers le dossier de cache des icônes (le dossier
_icons/). Cette alias doit pointer sur le même chemin que celui configuré
dans le script de synchronisation des icônes (par défaut à la racine :
./_icons). Il permet au composant Icon d'importer les icônes
dynamiquement.
Script de synchronisation
Le système inclut un script iconify-sync pour scanner votre code et télécharger automatiquement les icônes utilisées.
Configuration dans package.json
{
"scripts": {
"icon:scan": "tsx scripts/iconify-sync --path ./app",
"icon:watch": "tsx scripts/iconify-sync --path ./app --watch"
}
}Options
| Option | Raccourci | Description | Défaut |
|---|---|---|---|
--path <dir> | -p | Répertoire à scanner pour détecter les icônes | ./ |
--output <dir> | -o | Répertoire de sortie pour le cache des icônes | ./ |
--custom <dir> | -c | Répertoire contenant des fichiers JSON Iconify personnalisés | - |
--watch | -w | Active le mode watch pour re-scanner automatiquement à chaque modification | false |
--help | -h | Affiche l'aide et les exemples d'utilisation | - |
Exemples
# Scan unique du répertoire courant
tsx scripts/iconify-sync
# Scan d'un répertoire spécifique
tsx scripts/iconify-sync --path ./app
# Mode watch pour le développement
tsx scripts/iconify-sync --path ./app --watch
# Dossier de sortie personnalisé
tsx scripts/iconify-sync --path ./app --output ./custom-path
# Inclure des icônes personnalisées
tsx scripts/iconify-sync --custom ./brand-iconsDossier de cache personnalisé
Par défaut, les icônes sont stockées dans _icons/. Pour personnaliser :
// package.json
{
"scripts": {
"icon:scan": "tsx scripts/iconify-sync --output ./custom-path",
"icon:watch": "tsx scripts/iconify-sync --watch --output ./custom-path"
}
}N'oubliez pas de mettre à jour le path alias :
// tsconfig.json
{
"compilerOptions": {
"paths": {
"#icons": ["./custom-path/_icons"]
}
}
}Détection des icônes
Le script utilise une regex dynamique construite à partir de l'API Iconify pour détecter automatiquement toutes les références d'icônes au format "collection:icon-name".
Patterns supportés :
La détection fonctionne partout où vous utilisez une chaîne de caractères :
// ✅ Attributs JSX
<Icon name="lucide:star" />
<MyCustomIcon icon="heroicons:heart" />
// ✅ Variables et constantes
const icon = "tabler:settings"
const icons = ["mdi:home", "lucide:star"]
// ✅ Objets et appels de fonction
{ icon: "solar:sun-bold" }
getIcon("mdi:check")Patterns non supportés
// ❌ Valeurs dynamiques (variables, template literals)
<Icon name={`mdi:${icon}`} />;
// ❌ Collections invalides (non présentes dans Iconify)
("invalid-collection:icon");Performance
Le script de synchronisation offre des performances optimales même sur de grands projets grâce à la parallélisation du scan, le multiplexing HTTP/2 des téléchargements et un cache intelligent basé sur le modification time des fichiers.
Pour 1120 fichiers avec 6281 références d'icônes (110 uniques téléchargées), le script se termine en 465ms.
Icônes personnalisées
Ajoutez vos propres icônes (logos, assets custom) au format Iconify JSON. Pour convertir vos fichiers SVG existants :
# Installer @iconify/tools
pnpm add -D @iconify/tools
# Convertir un SVG
node scripts/svg-to-iconify.js input.svg output.json my-brand icon-nameLe script optimise le SVG et convertit les couleurs en currentColor.
Configuration Iconify Sync
Ajouter l'option --custom vers le dossier contenant vos icônes personnalisées au format JSON Iconify :
// package.json
{
"scripts": {
"icon:scan": "tsx scripts/iconify-sync --custom ./brand-icons",
"icon:watch": "tsx scripts/iconify-sync --watch --custom ./brand-icons"
}
}Utiliser vos icônes directement dans le composant Icon avec leur prefix et nom définis lors de la conversion :
<Icon name="brand:logo" className="size-8" />Validation automatique
Le système valide tous les fichiers et ignore silencieusement :
- JSON malformé ou non-Iconify (config.json, package.json, etc.)
- Fichiers sans
prefixouicons - Icônes sans propriété
bodyou avec types invalides
Seules les icônes valides au format Iconify sont chargées.
Utilisation sans React
Le cache d'icônes (_icons/) peut être utilisé avec d'autres frameworks. Le script iconify-sync scanne automatiquement les fichiers PHP en plus des fichiers JavaScript/TypeScript.
Laravel Blade
Créez un composant Blade pour une expérience similaire au composant Icon React :
// resources/views/components/icon.blade.php
@props(['name', 'size' => 24, 'class' => '', 'strokeWidth' => null])
@php
[$collection, $iconName] = explode(':', $name);
$path = base_path("_icons/{$collection}/{$iconName}.json");
$icon = file_exists($path) ? json_decode(file_get_contents($path)) : null;
$style = $strokeWidth !== null ? "--icon-sw-override: {$strokeWidth};" : '';
@endphp
@if($icon)
<svg
xmlns="http://www.w3.org/2000/svg"
width="{{ $size }}"
height="{{ $size }}"
viewBox="0 0 {{ $icon->width ?? 24 }} {{ $icon->height ?? 24 }}"
fill="currentColor"
@if($style) style="{{ $style }}" @endif
{{ $attributes->merge(['class' => $class]) }}
>{!! $icon->body !!}</svg>
@endif<x-icon name="lucide:home" />
<x-icon name="lucide:activity" :strokeWidth="1.5" />
<x-icon name="mdi:account" size="32" class="text-blue-500" />Recommandations
- Utiliser le script en environnement de développement : Il est recommandé d'utiliser le script uniquement en développement pour éviter des appels inutiles à l'API lors du build de production.
- Pas de tag d'icones dynamiques : Si le tag Iconify est généré dynamiquement, exemple :
`mdi:${iconName}`, il ne sera pas détecté par le script. - Pas de asChild avec Icon : Éviter d'utiliser le composant
IconavecasChildpour prévenir les erreurs d'hydratation SSR (voir section Configuration) - Extension VSCode : Pour une meilleure expérience dans votre IDE, utiliser l'extension Iconify IntelliSense
- Chercher les icônes : Acceder rapidement au icones que met à disposition l'API d'Iconify avec icones.js.org