Charger le CSS des blocs Gutenberg pour afficher le HTML brut, comme évoqué dans un précédent article, dépanne rapidement mais reste une solution de contournement : le front n’a alors aucun contrôle sur le rendu, aucune possibilité d’ajouter une interactivité propre à React (un carrousel, un lazy-load personnalisé), et reste dépendant du CSS généré par WordPress. Le mapping des blocs vers de véritables composants React va plus loin : chaque bloc devient une donnée structurée, interprétée et rendue par le framework front lui-même.
Cet article couvre le parsing du contenu en blocs, l’association de chaque type à un composant, et la gestion des blocs imbriqués ou inconnus. Il ne traite pas le chargement du CSS des blocs core, sujet distinct déjà couvert séparément.
Récupérer le contenu sous forme de blocs structurés
Par défaut, l’API REST WordPress renvoie le contenu déjà rendu en HTML dans content.rendered, sans structure de blocs exploitable directement. Pour obtenir la structure brute, deux options : exposer le champ content.raw (réservé aux utilisateurs autorisés à éditer, donc peu adapté à un usage public), ou parser le HTML rendu côté front avec un analyseur de blocs dédié comme @wordpress/block-serialization-default-parser, qui reconnaît les commentaires HTML délimitant chaque bloc (<!-- wp:paragraph -->).
import { parse } from '@wordpress/block-serialization-default-parser';
const blocs = parse(article.content.raw);
// [
// { blockName: 'core/paragraph', attrs: {}, innerHTML: '<p>...</p>', innerBlocks: [] },
// { blockName: 'core/image', attrs: { id: 42, url: '...' }, innerHTML: '...', innerBlocks: [] },
// ]
Sur un projet où exposer content.raw publiquement posait un problème d’accès, j’ai plutôt utilisé une route REST personnalisée côté WordPress qui appelle parse_blocks() (la fonction PHP native équivalente) et renvoie directement la structure JSON, évitant de dupliquer la logique de parsing côté front.
register_rest_field( 'post', 'blocs', array(
'get_callback' => function ( $object ) {
$post = get_post( $object['id'] );
return parse_blocks( $post->post_content );
},
) );
Un composant par type de bloc

Une fois la structure obtenue, chaque bloc est rendu par un composant React dédié, sélectionné dynamiquement selon blockName :
const COMPOSANTS_BLOCS = {
'core/paragraph': BlocParagraphe,
'core/heading': BlocTitre,
'core/image': BlocImage,
'core/quote': BlocCitation,
'core/list': BlocListe,
};
function RenduBloc({ bloc }) {
const Composant = COMPOSANTS_BLOCS[bloc.blockName];
if (!Composant) {
return <BlocInconnu bloc={bloc} />;
}
return <Composant attrs={bloc.attrs} innerHTML={bloc.innerHTML} />;
}
function RenduContenu({ blocs }) {
return blocs.map((bloc, i) => <RenduBloc key={i} bloc={bloc} />);
}
Exemple : le composant image
function BlocImage({ attrs }) {
return (
<figure>
<img src={attrs.url} alt={attrs.alt ?? ''} width={attrs.width} height={attrs.height} />
{attrs.caption && <figcaption>{attrs.caption}</figcaption>}
</figure>
);
}
Ce composant peut désormais utiliser un composant d’image optimisée propre au framework (comme next/image), avec lazy-loading et redimensionnement automatique, ce qu’un HTML brut affiché tel quel ne permet pas.
Gérer les blocs inconnus sans planter
Un site évolue : un auteur peut utiliser un bloc tiers (une extension installée après coup) ou un bloc core ajouté dans une version de WordPress plus récente que celle prise en compte par le mapping. Sans repli, le front planterait ou afficherait un espace vide silencieux. Le composant BlocInconnu doit au minimum afficher le HTML brut du bloc, en repli :
function BlocInconnu({ bloc }) {
if (process.env.NODE_ENV === 'development') {
console.warn(`Bloc non mappé : ${bloc.blockName}`);
}
return <div dangerouslySetInnerHTML={{ __html: bloc.innerHTML }} />;
}
L’avertissement en environnement de développement uniquement permet de repérer rapidement les blocs manquants pendant la phase de test, sans polluer la console en production.
Blocs avec enfants imbriqués
Certains blocs, comme core/columns, contiennent des blocs enfants dans innerBlocks, qu’il faut rendre récursivement plutôt qu’à plat :
function BlocColonnes({ bloc }) {
return (
<div style={{ display: 'flex', gap: '1.5rem' }}>
{bloc.innerBlocks.map((colonne, i) => (
<div key={i} style={{ flex: 1 }}>
<RenduContenu blocs={colonne.innerBlocks} />
</div>
))}
</div>
);
}
Comparaison avec l’affichage HTML brut
| Critère | HTML brut + CSS des blocs | Mapping en composants React |
|---|---|---|
| Effort de mise en place | Faible | Élevé, proportionnel au nombre de blocs utilisés |
| Contrôle du rendu et de l’interactivité | Aucun | Total, propre au framework front |
| Maintenance à l’ajout d’un nouveau bloc | Aucune (rendu automatiquement) | Nécessite d’ajouter le composant correspondant |
Je ne recommande le mapping complet que lorsque le front a réellement besoin d’interactivité propre sur certains blocs : pour un site éditorial simple sans composant interactif spécifique, le CSS des blocs core suffit souvent, avec un effort de maintenance bien moindre.
En résumé
Mapper les blocs Gutenberg vers des composants React demande de parser le contenu en structure de blocs, d’associer chaque type à un composant dédié, et de toujours prévoir un repli pour les blocs non mappés ou imbriqués. Cet effort supplémentaire, comparé à un simple affichage HTML brut, se justifie surtout quand le front doit ajouter une interactivité ou une optimisation que WordPress seul ne fournit pas.