Un bloc « carte témoignage » hérité, repris par Kaolin lors de la reprise de maintenance d’un site pour une agence immobilière, illustre un antipattern fréquent chez qui découvre l’API de blocs sans être passé par React auparavant : au lieu de décomposer le contenu en attributs distincts, le développeur d’origine avait tout capturé dans un unique attribut de type html, capturé depuis une div entière du save.
Ce qu’on voit
{
"attributes": {
"contenu": {
"type": "string",
"source": "html",
"selector": ".temoignage-contenu"
}
}
}
Dans l’éditeur, ce seul attribut contenait le nom du client, sa note, sa citation et le nom de son quartier, tous mélangés dans une structure HTML rigide générée par une unique zone RichText permettant un formatage libre. Rien n’empêchait un rédacteur de supprimer accidentellement la balise contenant la note en éditant le texte, ou de casser la mise en forme en collant du texte depuis un traitement de texte externe.
Pourquoi c’est un problème
Cette approche casse plusieurs promesses fondamentales de l’API de blocs. D’abord, il devient impossible de faire évoluer une seule donnée (par exemple ajouter un champ « date de l’avis ») sans risquer de casser le parsing du HTML existant sur tous les témoignages déjà publiés. Ensuite, aucune validation individuelle n’est possible : un champ note qui devrait être un nombre entre 1 et 5 est ici une portion de texte libre dans un blob HTML, sans aucune garantie de format. Enfin, ce type d’attribut interdit toute réutilisation de la donnée ailleurs (un Query Loop qui voudrait trier les témoignages par note, par exemple), puisqu’elle n’existe nulle part sous forme structurée, ni en attribut typé ni en post meta.

Quoi faire : décomposer en attributs typés
La refactorisation cible un schéma d’attributs explicite, chaque donnée ayant son propre type et sa propre source :
{
"attributes": {
"nomClient": { "type": "string", "default": "" },
"quartier": { "type": "string", "default": "" },
"note": { "type": "number", "default": 5 },
"citation": {
"type": "rich-text",
"source": "rich-text",
"selector": ".temoignage-citation"
}
}
}
Chaque champ devient éditable indépendamment dans l’inspecteur (un TextControl pour le nom, un RangeControl pour la note), avec une validation propre à chacun, et surtout une structure qui ne dépend plus d’un parsing fragile d’un unique bloc HTML.
Migrer sans tout casser : une déprécation, pas une réécriture brutale
Les témoignages déjà publiés utilisent l’ancien schéma. Une entrée deprecated avec une fonction migrate extrait les anciennes données du HTML brut vers les nouveaux attributs typés, en s’appuyant sur une expression régulière ciblée sur la structure connue de l’ancien rendu :
const ancienneVersion = {
attributes: { contenu: { type: 'string', source: 'html', selector: '.temoignage-contenu' } },
save({ attributes }) {
return <div className="temoignage-contenu">{/* ancien rendu */}</div>;
},
migrate({ contenu }) {
const nomMatch = contenu.match(/data-nom="([^"]*)"/);
const noteMatch = contenu.match(/data-note="(\d)"/);
return {
nomClient: nomMatch ? nomMatch[1] : '',
note: noteMatch ? parseInt(noteMatch[1], 10) : 5,
citation: contenu,
};
},
};
Une méthode progressive plutôt qu’un big bang
- Identifier d’abord toutes les données réellement distinctes cachées dans le blob HTML, sans chercher à tout migrer en une seule passe si le nombre de champs est élevé.
- Écrire la fonction
migrateet la tester sur un échantillon représentatif de contenus réels avant de la déployer. - Conserver l’ancienne entrée
deprecatedplusieurs versions, le temps que tous les contenus existants aient été rouverts au moins une fois dans l’éditeur pour déclencher la migration.
Un attribut de type html n’est pas un raccourci, c’est une dette qui se paie au moment où quelqu’un doit enfin lire la donnée qu’il contient.
Ce que cet article ne couvre pas
Le mécanisme formel des déprécations de blocs, sa syntaxe complète et ses subtilités (isEligible, ordre de résolution parmi plusieurs entrées) sont un sujet distinct déjà traité en détail ailleurs. Cet article se concentre sur le symptôme et la méthode de refactorisation, pas sur la mécanique de migration elle-même.
Notre verdict
Le bloc témoignage compte aujourd’hui onze attributs typés au lieu d’un unique blob HTML, et un nouveau champ (l’ajout d’une photo de profil) s’est intégré en une heure de travail, contre ce qui aurait nécessité une réécriture complète du parsing dans l’ancienne version.