vendredi 25 septembre 2026

À propos

Contact

Blocs Gutenberg

Un bloc fonctionne en local mais casse en prod après minification

Un identifiant généré dynamiquement côté JavaScript entrait en collision une fois les assets minifiés agressivement en production, un bug invisible en local.

Par Clément Hadrot • 16 juin 2026 • 4 min de lecture • Aucun commentaire
Un bloc fonctionne en local mais casse en prod après minification

Le bloc « accordéon FAQ » de la Librairie Miroir fonctionnait sans le moindre accroc sur les postes de développement et sur l’environnement de recette. Une fois déployé en production, où le processus de build appliquait une minification plus agressive des scripts que celle utilisée en recette, deux instances du même bloc sur une page produisaient un comportement erratique : cliquer sur une question ouvrait la réponse de l’autre accordéon, présent plus bas dans la page.

Symptôme : deux blocs, un seul identifiant

Le bloc générait un identifiant unique pour associer chaque question à sa réponse via des attributs aria-controls et id, nécessaires à l’accessibilité de l’accordéon. Cet identifiant était construit à partir d’un compteur de module incrémenté à chaque import du fichier JavaScript du bloc :

let compteur = 0;

function genererIdentifiant() {
  compteur += 1;
  return `accordeon-${compteur}`;
}

En local, ce module n’était chargé qu’une seule fois par page, le compteur s’incrémentant proprement à chaque instance du bloc. En production, le processus de minification et de regroupement des scripts (bundling) avait fusionné plusieurs points d’entrée en un seul fichier, mais avec une optimisation de tree-shaking qui, dans une configuration spécifique, dupliquait le module dans deux portées différentes du bundle final. Chaque copie du module partait donc de son propre compteur à zéro, réinitialisé indépendamment.

Pourquoi cela ne se voyait pas avant

Aucune erreur JavaScript n’était levée : le code s’exécutait normalement, produisait bien des identifiants, simplement identiques entre deux blocs qui auraient dû en recevoir des différents. Ce type de bug est particulièrement difficile à repérer en recette si l’environnement de recette n’utilise pas exactement la même configuration de minification que la production, ce qui était précisément le cas ici : la recette utilisait un build de développement moins agressif, qui ne déclenchait jamais la duplication de module.

L'essentiel à retenir : Un identifiant basé sur un compteur de module peut varier entre bundles ; crypto.randomUUID ou wp.element.useInstanceId évitent la collision ; Toujours tester avec les mêmes réglages de build qu'en production

Correctif : ne jamais dépendre d’un état de module partagé pour l’unicité

La correction retenue abandonne le compteur de module au profit de crypto.randomUUID(), disponible nativement dans tous les navigateurs modernes, qui ne dépend d’aucun état partagé entre modules :

function genererIdentifiant() {
  return `accordeon-${crypto.randomUUID()}`;
}

Pour un composant construit avec les éléments React de WordPress, useInstanceId du paquet @wordpress/compose reste une alternative pertinente, spécifiquement conçue pour ce cas d’usage à l’intérieur de l’écosystème de blocs :

import { useInstanceId } from '@wordpress/compose';

function Accordeon() {
  const instanceId = useInstanceId(Accordeon, 'accordeon');
  return <div id={instanceId}>{/* ... */}</div>;
}

useInstanceId génère un identifiant stable par instance de composant au sein du cycle de vie React, sans dépendre d’un compteur de module externe susceptible d’être dupliqué par le processus de build.

Reproduire le bug en local pour le vérifier

Le correctif ne pouvait être validé qu’en reconstruisant les assets avec exactement la configuration de production, via npm run build (utilisant @wordpress/scripts avec ses réglages de production), plutôt qu’avec npm run start qui sert des fichiers non minifiés en développement. Cette étape, ajoutée depuis à la checklist de déploiement, aurait permis de détecter le bug avant sa mise en production initiale.

  • Ne jamais générer un identifiant unique à partir d’un compteur de module partagé entre bundles.
  • Préférer crypto.randomUUID() ou useInstanceId selon le contexte, tous deux indépendants du processus de build.
  • Toujours tester au moins une fois avant déploiement avec la configuration de build exacte de la production, pas seulement celle de développement.

Un identifiant unique qui dépend de l’ordre de chargement des modules n’est pas unique, il est simplement optimiste.

Ce que ce cas ne couvre pas

La configuration générale du processus de build (webpack, réglages de minification, choix des outils) sort du périmètre de cet article, centré sur le comportement du bloc lui-même face à un environnement de production qu’il ne maîtrise pas.

En résumé

Deux blocs partageant le même identifiant après minification ont suffi à casser l’accessibilité de l’accordéon FAQ de la Librairie Miroir en production, alors que tout fonctionnait parfaitement en local. La leçon retenue : un identifiant unique ne doit jamais reposer sur un état partagé dont le comportement dépend du processus de build.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi