Sur le plugin de blocs maison de la Librairie Miroir, un réseau de librairies indépendantes, chacun des douze blocs comportait son propre contrôle de sélection de couleur d’accent, sa propre gestion de l’espacement interne, et son propre sélecteur d’image de fond, tous réécrits séparément avec des variations mineures d’un bloc à l’autre. Ajouter une option à l’un de ces contrôles supposait de la répliquer manuellement dans onze autres fichiers, avec le risque d’oubli que cela comporte.
Repérer les contrôles réellement dupliqués
La première étape n’a pas été technique mais d’inventaire : parcourir les douze composants InspectorControls et lister ce qui se répétait à l’identique ou presque. Trois candidats sont ressortis nettement : le sélecteur de couleur d’accent, le contrôle d’espacement, et le sélecteur d’image de fond avec son bouton de suppression. D’autres contrôles, spécifiques à un seul bloc, n’ont volontairement pas été extraits : l’extraction prématurée d’un contrôle utilisé une seule fois ajoute une indirection sans bénéfice.
Structure du dossier de composants partagés
Un dossier src/components a été créé en parallèle du dossier src/blocks, avec une arborescence organisée par contrôle plutôt que par bloc consommateur :
src/
├── blocks/
│ ├── bandeau-evenement/
│ ├── fiche-auteur/
│ └── selection-rayon/
└── components/
├── selecteur-couleur-accent/
│ └── index.js
├── controle-espacement/
│ └── index.js
└── selecteur-image-fond/
└── index.js
Des props explicites plutôt qu’un objet attributs entier
Le premier réflexe, tentant, aurait été de passer l’objet attributes et setAttributes du bloc tel quel à chaque composant partagé. Cette solution a été écartée volontairement : elle couple fortement le composant partagé au schéma d’attributs de chaque bloc consommateur, empêchant par exemple de nommer différemment l’attribut de couleur d’un bloc à l’autre. La règle retenue impose des props explicites :
function SelecteurCouleurAccent({ valeur, onChange, label = 'Couleur d\'accent' }) {
return (
<ColorPalette
value={valeur}
onChange={onChange}
disableCustomColors={false}
aria-label={label}
/>
);
}
// Dans un bloc consommateur :
<SelecteurCouleurAccent
valeur={attributes.couleurAccent}
onChange={(valeur) => setAttributes({ couleurAccent: valeur })}
/>

Chaque bloc reste ainsi seul responsable du nom de ses propres attributs et de la logique de mise à jour, tandis que le composant partagé ne connaît que l’interface minimale dont il a besoin : une valeur, un gestionnaire de changement, un libellé optionnel.
Gérer les variations mineures entre blocs
Le contrôle d’espacement, utilisé sur huit des douze blocs, présentait une variation : certains blocs autorisaient un espacement négatif (pour un chevauchement visuel volontaire), d’autres non. Plutôt que de créer deux composants quasi identiques, une prop autoriserNegatif a été ajoutée, avec une valeur par défaut à false :
function ControleEspacement({ valeur, onChange, autoriserNegatif = false }) {
return (
<RangeControl
label="Espacement"
value={valeur}
onChange={onChange}
min={autoriserNegatif ? -40 : 0}
max={80}
/>
);
}
Ce que cette extraction n’a pas cherché à résoudre
- Elle ne réorganise pas le plugin en plusieurs paquets publiés séparément, une échelle différente déjà couverte pour les projets qui gèrent plusieurs plugins de blocs en monorepo.
- Elle n’introduit aucune abstraction générique du type « constructeur de panneau d’inspecteur » : chaque bloc compose ses propres composants partagés dans l’ordre qui lui convient, sans configuration déclarative supplémentaire.
- Elle ne touche pas au rendu front des blocs, uniquement à l’expérience d’édition.
Un composant partagé qui connaît le nom des attributs d’un bloc en particulier n’est pas partagé, il est simplement dupliqué avec un niveau d’indirection en plus.
Notre verdict
L’extraction de trois composants a réduit le code des douze blocs de la Librairie Miroir de plus de six cents lignes cumulées, et surtout a rendu triviale l’ajout d’une option au sélecteur de couleur : une seule modification, propagée automatiquement à tous les blocs consommateurs, là où il aurait fallu auparavant modifier onze fichiers à la main.