Contenu principal

Optimiser le chargement des icones

lundi 7 août 2023

Il y a un mois, j’ai poussĂ© des changements sur les icones d’un site en production qui nous a fait gagnĂ© 8% sur le FCP de nos utilisateurices.

Pourtant, quand on a commencĂ© Ă  ajouter des icones, ils n’avaient aucun impact. Comment en sommes-nous arrivĂ©s là ? Dans cet article je vous prĂ©sente les diffĂ©rentes mĂ©thodes par lesquelles nous sommes passĂ©es, leurs avantages, leurs inconvĂ©nients et je terminerai avec un petit laius sur comment le mettre en place dans votre projet.

Cet article fait partie d’une sĂ©rie d’articles en lien avec la Web Performance oĂč j’essaye d’apporter des mĂ©thodes pour analyser la rapiditĂ© de vos pages et des solutions concrĂštes Ă  des problĂšmes rĂ©currents :

Si vous avez besoin d’une entrĂ©e en matiĂšre plus rapide, je suis aussi disponible en freelance pour vous accompagner sur des problĂ©matiques de performance.

Etape 1 : Une image par icone

Généralement quand on commence à développer un site web, on a encore aucun outil en place et on a juste une maquette à disposition. Alors le plus naturel est de commencer par utiliser une balise <img>.

<img src="/icone/home.svg" alt="Accueil" />

Histoire qu’elle ne soit pas pixelisĂ©e, on va prĂ©fĂ©rer le format SVG. On le passe un petit coup dans SVGOMG, un outil pour optimiser et rĂ©duire la taille de nos fichiers SVG. On vĂ©rifie que celle-ci est correctement mise en cache au niveau rĂ©seau. Et on envoie en prod.

Attention à la surcharge réseau

Cependant, le problĂšme de cette approche est qu’au fur et Ă  mesure cela risque de surcharger votre rĂ©seau. C’était trĂšs vrai Ă  l’époque de HTTP1. Beaucoup vous diront que ce n’est plus le cas pour HTTP2 mais ce n’est pas vrai. Au delĂ  d’un certain nombre de requĂȘtes simultanĂ©es des problĂšmes se manifestent :

  • le navigateur ne sait plus comment prioriser les requĂȘtes (quel fichier doit ĂȘtre tĂ©lĂ©chargĂ© en premier ?)
  • le navigateur peut perdre du temps en orchestration (cela se manifeste par beaucoup de hachure et une bandewidth saturĂ©e dans votre cascade rĂ©seau)
  • chaque fichier est gzippĂ© de maniĂšre indĂ©pendante : or, ces algorithmes de compression sont d’autant plus efficaces que vos fichiers sont gros et rĂ©pĂ©titifs. Vous gagnerez donc en bande passante en fusionnant vos icones plutĂŽt qu’en les tĂ©lĂ©chargeant sĂ©parĂ©ment

L’objectif est donc de trouver une maniùre de fusionner les icones en un fichier.

💡 Si vous n’ĂȘtes pas en mesure de changer de mĂ©thode de chargement d’icones, pour rĂ©gler cette problĂ©matique : assurez vous que vos icones qui ne sont pas directement visibles ont l’attribut loading="lazy", et vĂ©rifiez que vous ĂȘtes bien en HTTP2. Vous aurez adressĂ© le plus gros du problĂšme.

Interaction limitée

Une autre problĂ©matique de cette mĂ©thode est sa capacitĂ© Ă  changer d’image Ă  la volĂ©e.

NormalHover / Focus
Coeur noir Coeur coloré
Source d'icone : Font Awesome (en utilisant une <img> SVG)

Si vous voulez par exemple faire en sorte que votre icone change de couleur au survol, vous allez devoir :

  • ajouter du JavaScript pour changer l’URL de votre image Ă  la volĂ©e (ce qui provoquera des lenteurs lors de la transition parce qu’il faut tĂ©lĂ©charger la nouvelle image)
  • ou utiliser des astuces CSS Ă  base de Sprite
  • ou passer par filter en CSS (ex: de base votre image est colorĂ©e, et vous la passez en noir en appliquant filter: grayscale(100%) brightness(0);)

C’est somme toute assez compliquĂ©, et il existe des mĂ©thodes pour simplifier cela.

Etape 2 : les fonts d’icones

La premiùre solution à cela est l’utilisation de fonts d’icones.

A mon Ă©poque (j’ai le droit de dire ça aprĂšs 30 ans, n’en dĂ©plaise aux vieux đŸ˜€), la lib que tout le monde utilisait pour mettre des icones sur son site Ă©tait Font Awesome. C’était facile Ă  installer, ça permet d’ajouter en un clin d’oeil les icones.

Comment ça marche ?

  1. Un fichier de font qui permet d’associer un caractùre à un icone.

    @font-face {
    	font-family: 'Font Awesome 6 Free';
    	font-display: block;
    	src: url(/icons/font-awesome.woff2) format('woff2');
    }
    
  2. Une classe CSS qui permet de choisir quel caractùre afficher (ici le caractùre \f015 correspond à un icone 🏠)

    .fa {
    	font-family: 'Font Awesome 6 Free';
    	-moz-osx-font-smoothing: grayscale;
    	-webkit-font-smoothing: antialiased;
    	display: var(--fa-display, inline-block);
    	font-style: normal;
    	font-variant: normal;
    	line-height: 1;
    	text-rendering: auto;
    }
    .fa-house::before {
    	content: '\f015';
    }
    
  3. une balise HTML vide qui reprend la classe dĂ©finie en CSS pour choisir l’icone Ă  afficher

    <i class="fa fa-house"></i>
    

Avec ces trois Ă©lĂ©ments vous avez ce qu’il vous faut pour gĂ©rer des icones sur votre site. Ca fonctionne bien, et l’avantage supplĂ©mentaire par rapport aux balises <img> est que vous pouvez facilement configurer la couleur de vos icones en modifiant la couleur du texte en CSS :

:hover,
:focus {
	color: red;
}

Cependant, cette mĂ©thode aussi vient avec son lot d’inconvĂ©nients.

Pourquoi Ă©viter les fonts d’icones ?

Plus vous ajoutez des icones, plus vous avez des problĂšmes

Si votre base d’icone grossit (ou que vous utilisez une base toute faite) : les fichiers d’icones deviennent vite Ă©normes. A l’heure de la rĂ©daction de l’article, les icones gratuits de Font Awesome, c’est 148KB ! Sans compter le fichier CSS qui vient rajouter 24KB de plus.

Il est certes possible d’appliquer les mĂȘmes mĂ©thodes de subsetting que l’optimisation des fonts, mais le concept pousse quand mĂȘme Ă  utiliser une seule et mĂȘme source (le fichier woff2) qui grossiera tant que vous avez des icones Ă  ajouter.

D’ailleurs, dans un de mes projets, nous passions par fontastic pour gĂ©nĂ©rer la font. Au bout d’un certain nombre d’icones, la gĂ©nĂ©ration commençait Ă  crasher parce que nous avions trop d’icones. Peut-ĂȘtre que ça venait du fait qu’on avait juste trop d’icones, mais c’est quand mĂȘme l’indice d’un problĂšme 🙈

Lenteur d’affichage et gestion des fallbacks

Autre problĂšme : vos icones ne sont pas visibles tant que le fichier de font n’a pas Ă©tĂ© chargĂ©. Elles peuvent donc mettre du temps Ă  venir, d’autant plus si vous n’avez pas preload la font.

Plus gĂȘnant, Ă  une Ă©poque, la propriĂ©tĂ© font-display n’existait pas. Du coup, si la font n’arrivait pas Ă  se charger, on se retrouvait avec des caractĂšres bizarres affichĂ©s Ă  l’écran (ex : “”).

Conflit avec des outils d’accessibilitĂ©

Dans la majoritĂ© des documentations de font d’icones, vous devrez fouiller pour trouver la mention d’accessibilitĂ©. Pourtant, une icone, comme une image mĂ©rite d’ĂȘtre correctement labelisĂ©e. Cela est possible en adoptant ce HTML :

<!-- Si le picto est redondant avec un texte à cÎté -->
<i class="fa fa-house" aria-hidden="true"></i> Accueil

<!-- Si l'icone est seule -->
<span>
	<i class="fa-solid fa-house" title="Accueil" aria-hidden="true"></i>
	<span class="sr-only">Accueil</span>
</span>

En prenant le coup de main, c’est donc possible de correctement renseigner les labels pour les icones. Cependant, lĂ  oĂč c’est plus handicapant, c’est le fait que ce soit une font. En effet, certaines personnes surchargent les fonts utilisĂ©es dans les navigateurs pour en faciliter la lecture (ex : OpenDyslexic). Votre CSS ne sera donc plus maĂźtre de la font utilisĂ©e et risque d’afficher des lettres ou des caractĂšres unicodes Ă  la place des icones.

Etape 3 : Inline SVG Symbol

Ok, les font icones : pas ouf.

Si on accumule les contraintes, on cherche donc une solution :

  • qui Ă©vite des tonnes de requĂȘtes
  • qui ne passe pas par une font pour des questions de poids et d’accessibilitĂ©
  • qui soit facile Ă  changer lors d’une interaction

Est-ce qu’on pourrait utiliser du SVG directement dans la page ?

<svg height="1em" viewBox="0 0 24 24" aria-labelledby="icon-title-ajx18rtJBjSu">
	<title id="icon-title-ajx18rtJBjSu">Accueil</title>
	<path d="M0 24 v-24 h24 a12,12 11 0,1 0,24 a12,12 11 0,1 -24,0z" fill="currentColor" />
</svg>

Ainsi notre page HTML ne contient que les icones dont elle a besoin et cela a l’avantage de se charger immĂ©diatement, pas besoin de requĂȘte supplĂ©mentaire.

Cette mĂ©thode rĂ©pond Ă  la totalitĂ© des contraintes ci dessus. Par contre, si un icone est utilisĂ© plusieurs fois dans votre page, le HTML sera alourdi d’autant vu que vous allez devoir rĂ©pĂ©ter le SVG. C’est Ă  nuancer parce que cette duplication sera certainement compensĂ©e par gzip. Mais c’est toute de mĂȘme dommage alors qu’on peut faire mieux.

Utilisation des <defs> en SVG

En effet, en SVG il existe une mĂ©thode pour Ă©viter de dupliquer du code : l’utilisation des <symbol> avec <use>. Voyons comment ça marche.

Au sein de votre page HTML (gĂ©nĂ©ralement en fin de page, et en display: none), vous devez dĂ©finir une balise <svg> qui va contenir une liste de dĂ©finitions (<defs>). Cette liste de dĂ©finitions est gĂ©nĂ©ralement utilisĂ©e par votre image en SVG pour dĂ©crire des clipPath, des filtres ou des dĂ©gradĂ©s. Mais dans notre cas, nous allons utiliser des <symbol>. Grosso modo, un symbol est un sous SVG que l’on va pouvoir rĂ©fĂ©rencer ailleurs. Pour chaque icone, nous allons donc devoir ajouter une nouvelle balise <symbol> :

<svg xmlns="http://www.w3.org/2000/svg">
	<defs>
		<symbol id="icon-home" viewBox="0 0 24 24">
			<path d="M0 24 v-24 h24 a12,12 11 0,1 0,24 a12,12 11 0,1 -24,0z" fill="currentColor" />
		</symbol>
		<symbol id="icon-user" viewBox="0 0 24 24"><!-- ... --></symbol>
	</defs>
</svg>

Pour générer cette balise, il va falloir :

  1. Passer le SVG de l’icone initial dans SVGOMG afin de s’assurer qu’il est bien optimisĂ©
  2. Ouvrir ensuite l’onglet “Markup” pour y lire la balise <svg>
  3. Récupérer la viewBox de celle-ci et la mettre en attribut de votre balise <symbol>
  4. RĂ©cupĂ©rer tout ce qu’il y a Ă  l’intĂ©rieur de la balise <svg> et le mettre Ă  l’intĂ©rieur de votre balise <symbol>
  5. Définir enfin un attribut id sur votre balise <symbol> (ex: id="icon-home") Attention il est important que cet id soit unique dans toute votre page.

Une fois fait, passons Ă  l’affichage de l’icone dans notre page. En effet, pour l’instant, vous n’avez fait que dĂ©finir des rĂšgles d’affichage, mais vous n’avez pas indiquĂ© au navigateur oĂč l’afficher. Pour cela, nous allons dĂ©finir une nouvelle balise SVG que nous pouvons mettre dans notre HTML :

<svg height="32" width="32" viewBox="0 0 24 24" aria-labelledby="icon-title-ajx18rtJBjSu">
	<title id="icon-title-ajx18rtJBjSu">Accueil</title>
	<use href="#icon-home"></use>
</svg>

Celle-ci est trĂšs similaire au SVG Inline que nous avions Ă©crit plus haut. La seule diffĂ©rence est que nous remplaçons l’intĂ©rieur du <svg> (Ă  l’exception du <title>) par une balise <use>. Son attribut href permet en quelque sorte d’importer le symbol en y faisant rĂ©fĂ©rence via son id. Il se construit de la mĂȘme maniĂšre qu’une ancre dans une page.

Ainsi, le partie lourde du SVG (<path>, etc.) n’est pas rĂ©pĂ©tĂ©e dans votre page HTML, et vous pouvez facilement dĂ©finir de nouveaux icones au fur et Ă  mesure.

💡 Vous n’aimez pas dĂ©finir la liste des symbol manuellement ? N’hĂ©sitez pas Ă  fouiller internet pour trouver l’outil qui vous le gĂ©nĂ©rera pour vous. C’est par exemple le cas de Icon Pipeline.

HonnĂȘtement, vu que c’est 5min de manipulation et qu’on n’ajoute pas des icones tous les 4 matins, ce n’est pas une tĂąche que j’ai automatisĂ© sur les projets oĂč je travaille. Mais ça dĂ©pend de la taille de votre Ă©quipe et du type de profils qui manipulent le code.

La partie que l’on a automatisĂ© est la gĂ©nĂ©ration du SVG de l’icone lui mĂȘme (celui qui contient la balise <use>). N’hĂ©sitez pas Ă  abstraire cette partie derriĂšre une fonction ou un composant qui automatisera la gĂ©nĂ©ration d’ids, forcera l’utilisation d’un texte alternatif et de la dĂ©finition d’une taille.

Quels bĂ©nĂ©fices aux SVG Symbol pour gĂ©rer les icones ?

C’est quand mĂȘme pas mal de manutention. Alors quels avantages est-ce que ça apporte ?

Modifier la couleur du SVG en CSS

Un des avantages majeurs des fonts d’icones est la possibilitĂ© de changer la couleur de l’icone au survol.

C’est aussi possible avec les Symbol SVG. En effet, vous n’y avez peut-ĂȘtre pas prĂȘtĂ© attention, mais dans le <path> j’ai mis l’attribut fill="currentColor". currentColor est en fait une valeur en CSS qui permet de dire au navigateur d’utiliser la couleur courante du texte.

Ainsi, si votre icone est dans une balise <a> et que vous ajoutez le CSS suivant, alors au survol votre icone passera de bleu Ă  rouge.

a {
	color: blue;
}

a:hover,
a:focus {
	color: red;
}

Vous pouvez mĂȘme aller plus loin et manipuler plus en profondeur le style de votre SVG directement en CSS :

/* Par défaut, seule la bordure de l'icone est visible, en noir */
a svg {
	stroke: #000000;
	fill: transparent;
}

/* Au survol et au focus, la bordure devient rouge, et l'intérieur se remplit en rouge */
a:hover svg,
a:focus svg {
	stroke: red;
	fill: red;
}

C’est donc une mĂ©thode trĂšs versatile qui pourra vous amener bien plus loin dans l’animation de vos icones.

Les icones sont immédiatement visibles

Etant donnĂ© que l’icone est prĂ©sent dans le HTML, le navigateur est en mesure de parser votre SVG et l’afficher directement. L’inconvĂ©nient cependant est que le navigateur va du coup le parser de maniĂšre synchrone. Si vous avez beaucoup d’icones, cela peut finir par impacter vos performance. Dans notre cas, il s’agissait de 8% de FCP.

PossibilitĂ© d’importer uniquement les symbol dont a besoin la page

Etant donnĂ© que la liste de symbol est gĂ©nĂ©rĂ©e directement dans le HTML, il est possible de n’y mettre que les symbol dont la page a besoin et pas un de plus. Je le nuancerai en disant que ça dĂ©pend beaucoup de votre tooling. Dans notre cas, la liste de symbol a grossi jusqu’à atteindre 46 icones. C’était moins que nos icon fonts, mais c’était quand mĂȘme beaucoup (trop).

Quels inconvĂ©nients aux SVG Symbol ?

Plus vous avez d’icones, plus vous ralentirez le chargement de votre page.

MĂȘme si cette mĂ©thode a beaucoup de bĂ©nĂ©fices, j’ai commencĂ© Ă  mentionner ci-dessus que cela pouvait poser des problĂšmes dĂšs lors que le nombre d’icones diffĂ©rents utilisĂ©s dans votre page augmente (les fameux -8% de LCP quand nous avons arrĂȘtĂ© d’utiliser cette mĂ©thode).

Ce sera d’autant plus vrai si vos pages prĂ©sentent beaucoup d’interactivitĂ©. En effet, vous vous demandez peut-ĂȘtre comment on a pu se retrouver avec 46 icones diffĂ©rents sur chaque page. A tout cassĂ©, au premier affichage, nous en avions peut-ĂȘtre 10 maximum. D’oĂč venaient les 36 autres ? Ils Ă©taient utiles Ă  des modales globales. Nos outils n’étaient pas configurĂ©s pour importer des icones de maniĂšre asynchrone dans la page. Ainsi, les icones dont nous avions besoin pour les modales de connexion, d’inscription, de newsletter, etc. Ă©taient tous importĂ©s au chargement de la page plutĂŽt qu’au chargement de la modale.

💡 Je tiens Ă  prĂ©ciser que ce problĂšme venait principalement de la maniĂšre dont nous utilisions cette technique. Absoluement rien ne vous empĂȘche de configurer vos outils pour gĂ©rer l’ajout de maniĂšre asynchrone. Cependant, plutĂŽt que d’aller vers une solution technique complexe en JS, fetch, Ajax & co, l’étape 4 montrera comment rĂ©gler cette problĂ©matique simplement.

Les icones ne sont pas en cache

Contrairement aux fonts, les icones ne sont pas chargĂ©s via un fichier externe. La consĂ©quence est donc qu’à chaque nouvelle page HTML, vos utilisateurices devront retĂ©lĂ©charger la liste d’icones SVG au mĂȘme titre qu’ils tĂ©lĂ©chargerons le HTML. Dans notre cas, ça reprĂ©sentait entre 10 et 30kB supplĂ©mentaires par page, soit parfois jusqu’à 30% de poids de page supplĂ©mentaire.

Etape 4 : Remote SVG Symbol

Notre nouvelle solution devrait donc ajouter les contraintes suivantes :

  • qui Ă©vite des tonnes de requĂȘtes
  • qui ne passe pas par une font pour des questions de poids et d’accessibilitĂ©
  • qui soit facile Ă  changer lors d’une interaction
  • + qui puisse ĂȘtre tĂ©lĂ©chargĂ© Ă  la demande (ex : Ă  l’ouverture d’une modale)
  • + qui puisse ĂȘtre mis en cache entre chaque page

Pour rĂ©gler ces nouvelles contraintes, la solution est de faire en sorte que les icones soient rĂ©cupĂ©rĂ©s de maniĂšre externe. En effet, si nous reprenons la maniĂšre dont on a Ă©crit le SVG Ă  l’endroit oĂč on veut afficher l’icone, nous avions une balise use:

<use href="#icon-home"></use>

Celle-ci passe par un attribut href parce qu’en rĂ©alitĂ© on n’est pas obligĂ© de dĂ©finir une ancre dans la page courante. On peut aller chercher la dĂ©finition SVG Ă  une autre URL.

Ainsi plutît que de mettre votre SVG qui contient la liste de <defs> au sein de la page, nous allons l’enregistrer dans son propre fichier:

<!-- /images/icon-defs.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
	<defs>...</defs>
</svg>

A ce moment là, nous pourrons changer notre balise use afin qu’elle utilise la nouvelle URL:

-<use href="#icon-home"></use>
+<use href="/images/icon-defs.svg#icon-home"></use>

GrĂące Ă  cela, la liste d’icone se retrouve en dehors de la page HTML et pourra donc ĂȘtre tĂ©lĂ©chargĂ© et mis en cache au mĂȘme titre que n’importe quel asset externe.

⚠ Attention toutefois, il y a une contrainte forte : votre SVG distant doit impĂ©rativement ĂȘtre hĂ©bergĂ© sur le mĂȘme nom de domaine. Si ce n’est pas le cas, les navigateurs vont considĂ©rer que c’est une faille de sĂ©curitĂ© et votre icone ne marchera pas.

Ca tombe bien, c’est de toute façon une recommendation si vous voulez que votre site soit performant. Cela sera une raison de plus pour migrer vos assets sur votre nom de domaine principal si ce n’est pas dĂ©jĂ  le cas.

En vrai, il semble y avoir une mĂ©thode alternative qui existe si vraiment vous n’avez pas la maĂźtrise de vos noms de domaines. Cependant, elle offre moins de libertĂ© sur la gestion du lazyloading & des changements de couleur Ă  l’interaction.

💡 Comment est-ce que cela se comporte dans le cas des modales/tabs/dropdown ?

Je vous parlais des problĂšmes d’interactivité : on ne veut tĂ©lĂ©charger les icones que quand ils sont visibles. Si le HTML de votre modale n’est pas prĂ©sent initialement dans votre page, alors le fichier nĂ©cessaire Ă  l’affichage de son icone (ex : icon-modal-defs.svg) ne sera pas tĂ©lĂ©chargĂ©. Il existe toutefois une petite subtilitĂ© ici. Si votre HTML existe dĂ©jĂ  dans votre page mais est en display: none, alors votre fichier d’icones sera quand mĂȘme tĂ©lĂ©chargĂ©.

Ce n’est gĂ©nĂ©ralement pas quelque chose auquel vous serez confrontĂ© avec des frameworks modernes tels que React, Vue ou mĂȘme des solutions alternatives telles que Simulus/Turbo ou htmx. Mais si vous n’avez pas d’autres choix que d’avoir le HTML dĂ©jĂ  prĂ©sent dans votre page, essayez de l’entourer d’une balise <template>. Cela aura l’avantage de rendre tout le HTML Ă  l’intĂ©rieur de celle-ci inerte. Le navigateur ne parsera donc pas le SVG, et donc ne tĂ©lĂ©chargera pas le fichier rĂ©fĂ©rencĂ© par la balise <use>.

C’est de toute façon une bonne pratique parce que cela indiquera à votre navigateur qu’il n’a pas besoin de parser cette partie de la page pour pouvoir afficher le reste du contenu.

Quelles nouvelles opportunitĂ©s offrent les Remote SVG Symbol ?

Séparation en plusieurs fichiers

L’avantage de cette mĂ©thode est que vous pouvez rapidement sĂ©parer vos dĂ©finitions d’icones : il vous faut crĂ©er diffĂ©rents sets d’icones (ex : icon-global-defs.svg, icon-product-defs.svg, etc.) et modifier l’URL de la balise use en consĂ©quence.

Optimisation des icones critiques

Dans votre site, certains icones sont plus importants que d’autres. Vous avez par exemple certainement des icones dans votre menu que vous voulez afficher le plus rapidement. Mais les icones de rĂ©seau sociaux dans votre footer, un peu moins.

Avec cette solution, vous pouvez à nouveau gérer la priorisation de vos ressources.

Par exemple, vous pouvez faire en sorte que vos icones critiques restent en inline (en gĂ©nĂ©ral il y en a entre 2 et 5, ça n’alourdira pas de maniĂšre significative votre page). Pour cela, vous pouvez rester sur l’étape 3 uniquement pour ces icones. (C’est la solution que nous avons choisi dans le projet sur lequel je travaille.)

Ou alors, si la mise en cache est importante pour vous, vous pouvez considĂ©rer que ces icones feront partie d’un fichier icon-critical-defs.svg que vous pourrez preload dans votre page afin d’augmenter sa prioritĂ© de tĂ©lĂ©chargement.

Lazyloading des icones non critiques

A l’inverse, pour les icones qui sont beaucoup plus loin dans votre page, si vous mesurez que les Remote SVG Symbol ralentissent le chargement de votre page, n’hĂ©sitez pas Ă  mettre en place une mĂ©thode de lazyloading. Malheureusement, il n’en existe pas de native Ă  ce jour qui mimiquerai le comportement de loading="lazy" sur une balise img.

Mais vous pourriez imaginer mettre en place un mécanisme en JavaScript. Cela pourrait ressembler à :

const lazyloadIconObserver = new IntersectionObserver((entries) => {
	entries.forEach((entry) => {
		if (entry.isIntersecting) {
			const useTags = entry.target.querySelectorAll('use[data-href]');
			useTags.forEach((tag) => {
				tag.setAttribute('href', tag.dataset.href);
			});
			lazyloadIconObserver.unobserve(entry.target);
		}
	});
});

const lazyIcons = document.querySelectorAll('svg.lazy-icon');
lazyIcons.forEach((icon) => {
	lazyloadIconObserver.observe(icon);
});

Il vous faudrait alors changer l’endroit oĂč l’icone est affichĂ© ainsi :

-<svg height="32" width="32" viewBox="0 0 24 24" aria-labelledby="icon-title-ajx18rtJBjSu">
+<svg class="lazy-icon" height="32" width="32" viewBox="0 0 24 24" aria-labelledby="icon-title-ajx18rtJBjSu">
 	<title id="icon-title-ajx18rtJBjSu">Accueil</title>
-	<use href="#icon-home"></use>
+	<use data-href="/images/icon-defs.svg#icon-home"></use>
 </svg>

Comment mettre en place ces mĂ©thodes quand je code en React ?

Jusqu’à maintenant, je vous ai montrĂ© comment le principe de base fonctionne : quel est le HTML/CSS que vous aurez besoin d’ajouter dans votre page pour que ça fonctionne. Cependant, selon votre niveau d’aisance avec le tooling que vous utilisez, peut-ĂȘtre que la mise en place sera douloureuse.

C’est d’autant plus vrai qu’une pratique assez rĂ©pandue dans l’écosystĂšme React est d’utiliser un loader webpack pour transformer votre SVG en composant React/JSX.

La consĂ©quence, comme le montre Jason “developit” Miller dans son tweet, est que vous pouvez vous retrouver avec votre SVG Ă  la fois dans votre HTML et dans votre JS. Dans le cas qu’il a trouvĂ©, 50% du JS Ă©tait en fait du SVG, soit 125KB, avec les consĂ©quences en terme de performance qui vont avec. Oops.

Pour Ă©viter ces problĂšmes, vous devrez donc configurer votre bundler pour faire en sorte que votre SVG soit traitĂ© comme une image classique. Ainsi, dans votre code, quand vous importerez le SVG qui contient la liste de dĂ©finitions, vous ne rĂ©cupĂ©rerez pas le contenu du SVG, mais une URL oĂč il sera accessible. Cette mĂȘme URL dont on a besoin avec la mĂ©thode Remote SVG Symbol.

Pour Webpack (5+), vous aurez besoin de modifier votre configuration pour configurer les fichiers SVG en tant que asset/ressource.

// webpack.config.js
const config = {
	// 

	module: {
		rules: [
			// 

			{
				test: /\.svg/,
				type: 'asset/resource'
			}
		]
	}
};

Pour Vite, c’est dĂ©jĂ  le comportement par dĂ©faut.

Ensuite, dans votre code React, vous pourrez écrire vos Icons de cette façon :

import IconDefs from './home-icon-defs.svg';

const Icon = ({ icon, title, size }) => {
	/* J'inclue ici la gestion du texte alternatif de l'icone */
	const randId = Math.random() * Number.MAX_SAFE_INTEGER;
	const titleId = `icon-title-${randId}`;
	const pixelSize = size === 'big' ? 32 : 24;

	return (
		<svg height={pixelSize} width={pixelSize} viewBox="0 0 24 24" aria-labelledby={titleId}>
			<title id={titleId}>{title}</title>
			{/* Mais la partie qui nous intéresse est surtout ce use */}
			<use href={`${IconDefsUrl}#icon-${icon}`}></use>
		</svg>
	);
};

// 


<Icon icon="home" title="Accueil" size={32} />;

Ainsi, vous utilisez bel et bien un use et votre quantitĂ© de JS ne se retrouve pas pĂ©nalisĂ©e par le nombre d’icones que vous avez dans votre SVG.

Pour approfondir cette partie, n’hĂ©sitez pas Ă  jeter un oeil Ă  Breaking Up with SVG-in-JS in 2023 (EN).

Récapitulatif

Ok donc je vous ai montrĂ© 4 façons de faire des icones. Chacune a ses avantages et inconvĂ©nients et comme d’habitude il n’y a pas de silver bullet.

Si on devait résumer tout ça dans une matrice de décision :

  • Si l’icone est utilisĂ© une seule fois dans votre page,
    • Si cet icone est critique ou a besoin de changer de couleur lors d’une interaction,
    • Si non,
  • Si l’icone est utilisĂ©e plusieurs fois,
  • Si vous utilisez une font d’icone,
    • âžĄïž repartez en haut de cette matrice et choisissez une autre mĂ©thode. Vos utilisateurices vous remercieront.

Ce qu’il faut aussi retenir, c’est que tant que vous avez une quantitĂ© limitĂ©e d’icones, utiliser une balise SVG inline ou une balise <img> sera certainement largement suffisant. N’hĂ©sitez donc pas Ă  mesurer votre site avant de vous lancer dans des optimisations prĂ©maturĂ©es.

Vous avez du mal Ă  dĂ©terminer quelle est la meilleure solution pour vous ou sur quels chantiers de performance vous lancer ? Contactez-moi. Nous pouvons certainement travailler ensemble afin de rendre votre site rapide et amĂ©liorer votre taux de conversion.

Si vous avez trouvĂ© tout cela fort intĂ©ressant et ĂȘtes curieuse·x de connaĂźtre la suite, n’hĂ©sitez pas Ă  me suivre ou Ă  me poser des questions sur Mastodon ou Twitter. Notamment, dans mon prochain article, je parlerai des diffĂ©rentes stratĂ©gies pour diminuer votre quantitĂ© de JavaScript.

Pour les plus habitué·e·s d’entre vous, sachez que je publierai cette suite dans 2 semaines, ne pouvant pas assurer la rĂ©daction la semaine prochaine. Je vous souhaite un bel Ă©tĂ© et de bonnes vacances Ă  celleux qui en ont et vous dit Ă  trĂšs vite ⛱


Si vous voulez suivre mes publications, il paraĂźt que j'ai un feed RSS, Mastodon et un Twitter.

Si vous pensez à d'autres méthodes que vous voudriez que je mette en place (pigeon voyageur, avion en papier, etc.), n'hésitez pas à me les proposer :)