Meilleures pratiques React SVG : Guide complet de toutes les méthodes d'implémentation

Guide complet pour utiliser SVG dans React - comparant toutes les méthodes incluant la balise img, SVG inline, SVGR, bibliothèques d'icônes et imports dynamiques avec avantages, inconvénients et exemples réels

Meilleures pratiques React SVG : Guide complet de toutes les méthodes d'implémentation

SVG est essentiel pour les applications React modernes, mais il existe plusieurs façons de l'implémenter - chacune avec des compromis différents. Dans ce guide complet, nous explorerons toutes les méthodes, comparerons leurs performances et fournirons des exemples réels pour vous aider à choisir la meilleure approche.

5 façons d'utiliser SVG dans React

Méthode 1 : Balise <img>

Méthode 2 : SVG inline

Méthode 3 : SVGR (Import de composant)

Méthode 4 : Bibliothèques de composants d'icônes

Méthode 5 : Chargement dynamique SVG

Explorons chaque méthode en détail.


Méthode 1 : Balise <img>

Implémentation

// Usage simple
function Logo() {
  return <img src="/logo.svg" alt="Logo de l'entreprise" width="200" height="50" />
}
 
// Avec import
import logoUrl from './logo.svg'
 
function Logo() {
  return <img src={logoUrl} alt="Logo de l'entreprise" className="w-48 h-12" />
}
 
// TypeScript : ajouter à vite-env.d.ts ou globals.d.ts
declare module '*.svg' {
  const content: string
  export default content
}

Avantages

Méthode la plus simple - Aucune configuration spéciale nécessaire ✅ Mise en cache du navigateur - Les fichiers SVG sont mis en cache séparément ✅ Chargement différé - Utilisez loading="lazy" pour les performances ✅ Pas de gonflement du bundle - Le SVG reste comme fichier séparé ✅ Syntaxe familière - Fonctionne comme n'importe quelle autre image

// Exemple de chargement différé
<img
  src="/hero-illustration.svg"
  alt="Héros"
  loading="lazy"
  decoding="async"
/>

Inconvénients

Pas de style CSS - Ne peut pas changer les couleurs de remplissage/trait ❌ Pas de contrôle JavaScript - Ne peut pas animer des parties individuelles ❌ Requête HTTP supplémentaire - Un fichier de plus à télécharger ❌ Pas de comportement responsive - Ne peut pas masquer/afficher des parties selon la viewport ❌ Accessibilité limitée - Seulement le texte alt, pas de structure sémantique

// ❌ Cela ne fonctionnera pas avec la balise <img>
<img
  src="/icon.svg"
  className="fill-blue-500"  // Ne changera pas la couleur SVG
/>

Quand l'utiliser

Logos statiques qui n'ont pas besoin de changements de couleur ✅ Grandes illustrations qui gonfleraient votre bundle ✅ Images décoratives qui n'ont pas besoin de détails d'accessibilité ✅ SVG externes depuis des CDNs ou sources tierces

Exemple de performance

// ✅ Bon : Grand SVG qui augmenterait la taille du bundle
function HeroSection() {
  return (
    <div className="hero">
      <img
        src="/hero-illustration.svg"
        alt="Illustration du produit"
        width="800"
        height="600"
        loading="lazy"
      />
    </div>
  )
}
 
// Taille du fichier : Le SVG reste séparé (ex: 45 Ko)
// Impact sur le bundle : 0 Ko

Méthode 2 : SVG inline

Implémentation

function HomeIcon() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="currentColor"
      className="w-6 h-6"
    >
      <path d="M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69a.75.75 0 1 0 1.06-1.061l-8.689-8.69a2.25 2.25 0 0 0-3.182 0l-8.69 8.69a.75.75 0 1 0 1.061 1.06l8.69-8.689Z" />
      <path d="m12 5.432 8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 0 1-.75-.75v-4.5a.75.75 0 0 0-.75-.75h-3a.75.75 0 0 0-.75.75V21a.75.75 0 0 1-.75.75H5.625a1.875 1.875 0 0 1-1.875-1.875v-6.198a2.29 2.29 0 0 0 .091-.086L12 5.432Z" />
    </svg>
  )
}

Avantages

Contrôle CSS complet - Changez les couleurs, animations, tout ✅ Pas de requêtes HTTP - Le SVG est dans votre bundle ✅ Manipulation JavaScript - Animez des parties, répondez aux événements ✅ Comportement responsive - Affichez/masquez des éléments selon l'état ✅ Accessibilité complète - Utilisez <title>, <desc>, attributs ARIA ✅ Support de thème - Utilisez currentColor pour le thémage

// ✅ Exemple de contrôle complet
function ThemedIcon({ className }: { className?: string }) {
  const [isHovered, setIsHovered] = useState(false)
 
  return (
    <svg
      viewBox="0 0 24 24"
      className={className}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <title>Paramètres</title>
      <desc>Cliquez pour ouvrir le panneau de paramètres</desc>
      <path
        fill={isHovered ? '#3B82F6' : 'currentColor'}
        style={{ transition: 'fill 0.3s' }}
        d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"
      />
    </svg>
  )
}

Inconvénients

Augmentation de la taille du bundle - Le code SVG est dans votre bundle JS ❌ Code verbeux - Les grands chemins SVG encombrent les composants ❌ Plus difficile à maintenir - Éditer le code SVG est fastidieux ❌ Pas de mise en cache du navigateur - Le SVG change quand le bundle change ❌ Répétition - Même icône utilisée plusieurs fois = code dupliqué

// ❌ Mauvais : Grand SVG gonfle le composant
function ComplexIllustration() {
  return (
    <svg viewBox="0 0 500 500">
      {/* 200+ lignes de données de chemin */}
      <path d="M123.45 67.89 L456.78..." />
      <path d="M234.56 78.90 L567.89..." />
      {/* ... 198 autres chemins */}
    </svg>
  )
}
 
// Taille du fichier : Ajoute 15 Ko au bundle
// Maintenabilité : Très faible

Quand l'utiliser

Petites icônes (< 1 Ko) qui ont besoin de style ✅ SVG animés qui ont besoin de contrôle JavaScript ✅ Couleurs changeant fréquemment selon le thème/l'état ✅ SVG interactifs avec effets de survol/clic ✅ SVG critiques nécessaires immédiatement (pas de délai de chargement)

Optimisation des performances

// ✅ Bon : Extraire dans un composant séparé
const StarIcon = () => (
  <svg viewBox="0 0 20 20" fill="currentColor">
    <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
  </svg>
)
 
// Utiliser avec le thémage
function Rating({ stars }: { stars: number }) {
  return (
    <div className="flex text-yellow-400">
      {Array.from({ length: stars }).map((_, i) => (
        <StarIcon key={i} />
      ))}
    </div>
  )
}

Méthode 3 : SVGR (Import de composant)

Configuration

1. Installer SVGR pour Vite

npm install vite-plugin-svgr

2. Configurer vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import svgr from 'vite-plugin-svgr'
 
export default defineConfig({
  plugins: [
    react(),
    svgr({
      // Options SVGR
      svgrOptions: {
        icon: true, // Utiliser 1em comme taille par défaut
        dimensions: false, // Supprimer les attributs width/height
        typescript: true, // Générer les types TypeScript
      },
    }),
  ],
})

3. Types TypeScript

// vite-env.d.ts
/// <reference types="vite-plugin-svgr/client" />
 
declare module '*.svg' {
  import type { FunctionComponent, SVGProps } from 'react'
  export const ReactComponent: FunctionComponent<SVGProps<SVGSVGElement>>
  const src: string
  export default src
}

Implémentation

// Importer comme composant React
import { ReactComponent as Logo } from './logo.svg'
// Ou import nommé
import LogoIcon from './logo.svg?react'
 
function Header() {
  return (
    <div className="header">
      <Logo className="w-32 h-8 text-blue-600" />
      {/* Support complet des props */}
      <Logo
        width={128}
        height={32}
        fill="currentColor"
        aria-label="Logo de l'entreprise"
      />
    </div>
  )
}

Avantages

Le meilleur des deux mondes - Séparation des fichiers + contrôle complet ✅ Auto-optimisation - SVGR nettoie le SVG à l'importation ✅ Support TypeScript - Sécurité de type complète avec SVGProps ✅ Code propre - Le SVG reste dans un fichier séparé ✅ Support des props - Passez className, fill, stroke, etc. ✅ Tree shaking - Les SVG non utilisés ne sont pas inclus dans le bundle ✅ Support currentColor - Thémage facile

// ✅ Sécurité de type avec autocomplétion
import { ReactComponent as Icon } from './icon.svg'
 
function MyButton() {
  return (
    <button className="flex items-center gap-2">
      <Icon
        className="w-5 h-5"
        // TypeScript montre toutes les props SVG valides
        aria-hidden="true"
        focusable="false"
      />
      Cliquez-moi
    </button>
  )
}

Inconvénients

Configuration de build nécessaire - Nécessite la configuration Vite/Webpack ❌ Taille du bundle - Le SVG est dans le bundle (comme inline) ❌ Courbe d'apprentissage - Comprendre la syntaxe d'import et la configuration ❌ Temps de build - Les SVG sont traités pendant le build

Quand l'utiliser

Bibliothèques d'icônes que vous construisez ✅ Composants réutilisables dans votre application ✅ Projets type-safe avec TypeScript ✅ Outils de build modernes (Vite, Webpack, etc.) ✅ SVG de taille moyenne (1-10 Ko) ayant besoin de props

Exemple réel

// icons/index.ts - Exports d'icônes centralisés
export { ReactComponent as HomeIcon } from './home.svg'
export { ReactComponent as UserIcon } from './user.svg'
export { ReactComponent as SettingsIcon } from './settings.svg'
 
// components/Navigation.tsx
import { HomeIcon, UserIcon, SettingsIcon } from '@/icons'
 
function Navigation() {
  const iconClass = "w-6 h-6 transition-colors"
 
  return (
    <nav className="flex gap-4">
      <a href="/" className="group">
        <HomeIcon className={`${iconClass} group-hover:text-blue-500`} />
      </a>
      <a href="/profile" className="group">
        <UserIcon className={`${iconClass} group-hover:text-blue-500`} />
      </a>
      <a href="/settings" className="group">
        <SettingsIcon className={`${iconClass} group-hover:text-blue-500`} />
      </a>
    </nav>
  )
}

Méthode 4 : Bibliothèques de composants d'icônes

Bibliothèques populaires

# Heroicons
npm install @heroicons/react
 
# Lucide
npm install lucide-react
 
# React Icons (agrégateur - inclut Font Awesome, Material, etc.)
npm install react-icons
 
# Phosphor Icons
npm install @phosphor-icons/react

Exemples d'implémentation

Heroicons

import { HomeIcon, UserIcon } from '@heroicons/react/24/outline'
import { HomeIcon as HomeIconSolid } from '@heroicons/react/24/solid'
 
function Navigation() {
  return (
    <div className="flex gap-4">
      {/* Version contour */}
      <HomeIcon className="w-6 h-6 text-gray-600" />
 
      {/* Version pleine */}
      <HomeIconSolid className="w-6 h-6 text-blue-500" />
 
      {/* Version mini (16x16) */}
      <UserIcon className="w-4 h-4" />
    </div>
  )
}

Lucide React

import { Heart, Star, ShoppingCart } from 'lucide-react'
 
function ProductCard() {
  return (
    <div className="card">
      <Heart
        size={24}
        color="red"
        fill="pink"
        strokeWidth={1.5}
      />
 
      <Star size={20} fill="gold" stroke="orange" />
 
      {/* Contrôle absolu de la taille */}
      <ShoppingCart
        size={32}
        className="cursor-pointer hover:text-blue-500"
        onClick={() => console.log('Ajouter au panier')}
      />
    </div>
  )
}

React Icons (Universel)

// Mélanger des icônes de différentes bibliothèques
import { FaGithub, FaTwitter } from 'react-icons/fa' // Font Awesome
import { MdEmail } from 'react-icons/md' // Material Design
import { IoLogoReact } from 'react-icons/io5' // Ionicons
 
function SocialLinks() {
  return (
    <div className="flex gap-3">
      <FaGithub size={24} />
      <FaTwitter size={24} className="text-blue-400" />
      <MdEmail size={24} />
      <IoLogoReact size={24} className="text-cyan-500" />
    </div>
  )
}

Avantages

Zéro configuration - Installez et utilisez simplement ✅ Design cohérent - Toutes les icônes correspondent ✅ Support TypeScript - Sécurité de type complète ✅ API Props - size, color, className, etc. ✅ Tree shaking - Seules les icônes importées dans le bundle ✅ Mises à jour régulières - De nouvelles icônes ajoutées fréquemment ✅ Documentation - Galeries d'icônes consultables

Inconvénients

Taille du bundle - Chaque icône ajoute au bundle ❌ Dépendance - Package externe à maintenir ❌ Personnalisation limitée - Ne peut pas éditer les chemins des icônes ❌ Contraintes de style - Verrouillé au design de la bibliothèque

Quand l'utiliser

Prototypage rapide - Commencez rapidement ✅ Système de conception cohérent - Besoin d'icônes correspondantes ✅ Motifs UI courants - Icônes standard (accueil, utilisateur, paramètres) ✅ Ne veut pas gérer les icônes - Laissez la bibliothèque gérer les mises à jour ✅ Projets TypeScript - Veut une sécurité de type complète


Méthode 5 : Chargement dynamique SVG

Implémentation

import { useState, useEffect } from 'react'
 
function DynamicSVG({ name }: { name: string }) {
  const [svg, setSvg] = useState<string>('')
 
  useEffect(() => {
    // Importer dynamiquement le SVG
    import(`./icons/${name}.svg?raw`)
      .then(module => setSvg(module.default))
      .catch(err => console.error('Échec du chargement du SVG :', err))
  }, [name])
 
  return <div dangerouslySetInnerHTML={{ __html: svg }} />
}
 
// Usage
function App() {
  return <DynamicSVG name="home" />
}

Avantages

Chargement flexible - Chargez les icônes à la demande ✅ Réduire le bundle initial - Icônes chargées quand nécessaire ✅ Contenu dynamique - Chargez différentes icônes selon les données ✅ Mise en cache - Peut implémenter une stratégie de cache personnalisée

Inconvénients

Complexité - Plus de code à maintenir ❌ Sécurité de type - Plus difficile à vérifier les types ❌ États de chargement - Besoin de gérer le chargement/erreur ❌ Risque XSS - Utilisation de dangerouslySetInnerHTMLRequêtes supplémentaires - Surcharge réseau

Quand l'utiliser

Contenu piloté par CMS - Les icônes viennent d'une API ✅ Systèmes de plugins - Icônes téléchargées par l'utilisateur ✅ Très grands ensembles d'icônes - Ne peut pas tout mettre en bundle ✅ Rendu conditionnel - Charger seulement si nécessaire


Tableau comparatif

MéthodeTaille du bundleStyleTypeScriptMise en cacheConfigurationIdéal pour
<img>✅ Aucune❌ Non⚠️ Limité✅ Oui✅ FacileLogos statiques, grands fichiers
SVG inline❌ Augmente✅ Complet✅ Oui❌ Non✅ FacilePetites icônes interactives
SVGR❌ Augmente✅ Complet✅ Complet❌ Non⚠️ Config requiseComposants réutilisables
Bibliothèques d'icônes⚠️ Par icône✅ Bon✅ Complet❌ Non✅ FacileDéveloppement rapide
Chargement dynamique✅ Minimal⚠️ Limité❌ Difficile✅ Personnalisé❌ ComplexeContenu CMS

Recommandations par cas d'usage

Petit projet / Page d'accueil

Utilisez : Bibliothèque d'icônes (Heroicons ou Lucide)

import { HomeIcon, UserIcon, Cog6ToothIcon } from '@heroicons/react/24/outline'
 
// Simple, rapide, aucune configuration nécessaire

Système de conception / Bibliothèque de composants

Utilisez : SVGR pour les icônes personnalisées

// Centralisé, type-safe, réutilisable
export { ReactComponent as BrandIcon } from './brand.svg'
export { ReactComponent as CustomIcon } from './custom.svg'

Grande application

Utilisez : Combinaison

// Icônes : Bibliothèque (Heroicons)
import { HomeIcon } from '@heroicons/react/24/outline'
 
// Logo : balise <img> (mis en cache séparément)
<img src="/logo.svg" alt="Logo" />
 
// Illustrations personnalisées : SVGR
import { ReactComponent as Hero } from './hero-illustration.svg'

Application critique pour les performances

Utilisez : balise <img> + chargement différé

<img
  src="/illustration.svg"
  loading="lazy"
  decoding="async"
/>

Conclusion

Guide de décision rapide

Choisissez <img> si :

  • ✅ Grands fichiers SVG (> 10 Ko)
  • ✅ Pas besoin de changements de couleur/style
  • ✅ Logos ou illustrations statiques

Choisissez SVG inline si :

  • ✅ Très petites icônes (< 500 octets)
  • ✅ Besoin d'animations CSS
  • ✅ Icônes personnalisées uniques

Choisissez SVGR si :

  • ✅ Construction d'un système de conception
  • ✅ Besoin du support TypeScript
  • ✅ Icônes personnalisées réutilisables

Choisissez les bibliothèques d'icônes si :

  • ✅ Développement rapide
  • ✅ Icônes UI standard
  • ✅ Ne veut pas gérer les icônes

Choisissez le chargement dynamique si :

  • ✅ Contenu piloté par CMS
  • ✅ Très grandes collections d'icônes
  • ✅ Icônes depuis API/base de données

Recommandation finale

Pour la plupart des applications React modernes en 2025 :

  1. Bibliothèque d'icônes (Heroicons/Lucide) pour les icônes UI standard
  2. SVGR pour les icônes de marque personnalisées et illustrations
  3. Balise <img> pour les grands SVG décoratifs

Cette combinaison offre le meilleur équilibre entre expérience de développement, performance et flexibilité.

Bon codage !