Formation « Développer des blocs Gutenberg sur mesure avec React »

Imbriquer des blocs grâce à InnerBlocks et répéteurs

Lecture : 8 minutes • 8

Afin d’aller encore plus loin, on va voir comment insérer des blocs dans nos blocs grâce au composant InnerBlocks. Et vous verrez que c’est hyper facile. On va donc pouvoir créer des blocs structurels, et choisir quels blocs pourront être utilisés à l’intérieur.

Lorsque vous utilisez le bloc groupe, bannière ou encore colonne de WordPress, vous pouvez insérer à l’intérieur d’autres blocs. On obtient alors des blocs imbriqués les uns dans les autres :

Le bloc bannière et ses blocs enfants, comme le montre la hiérarchie dans la Vue en Liste, à gauche de l'image.
Le bloc Bannière accepte des blocs enfants

On peut très observer cette indentation grâce à la vue en liste, à gauche.

Dans ce cours, nous allons voir comment mettre en place un bloc conteneur qui acceptera des blocs enfants, et on verra notamment comment limiter le choix de ceux qui pourront être insérés.

Créer un conteneur de blocs avec InnerBlocks

Pour commencer, on va créer un bloc conteneur tout simple grâce au composant <InnerBlocks /> de WordPress et vous allez voir comme c’est simple de l’utiliser.

Pour ceux qui ont la version premium de la formation, vous trouverez le code du bloc dans le dossier /src/08-innerblocks/.

Le composant InnerBlocks

Pour déclarer un composant acceptant des bloc enfants, rien de plus simple donc.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Voici le code minimum à écrire pour cela :

JSX
/src/08-innerblocks/edit.js

Et c’est tout ! On a simplement besoin d’appeler le composant InnerBlocks à l’endroit où on souhaite ajouter la possibilité d’insérer des blocs.

Ce composant est importé au début du fichier, au même titre que useBlockProps, à partir de la librairie @wordpress/block-editor.

Pour nous rendre compte du résultat, j’ai ajouté un liseret coloré autour du conteneur ainsi qu’une marge interne à partir du CSS :

Sass
/src/08-innerblocks/style.scss

Lorsque vous insérez ce bloc dans le contenu d’une publication, vous allez pouvoir insérer à l’intérieur d’autres blocs, grâce au bouton + qui apparait en bas à droite lorsque celui-ci est sélectionné dans l’éditeur :

Le bloc conteneur que l'on a créé a plusieurs blocs enfants à l'intérieur : image, texte et bouton.
Notre bloc conteneur avec des blocs à l’intérieur

Du côte du save.js, pour l’enregistrement, c’est pas bien plus compliqué non plus.

JSX
/src/08-innerblocks/save.js

Cette fois, on utilise InnerBlocks.Content, de la même manière que RichText.Content, afin de permettre à WordPress de préparer et traiter le contenu à afficher et le convertir en HTML prêt à l’enregistrement.

Comme vous l’avez peut-être remarqué, on n’a même pas besoin de définir d’attributs pour ce bloc, Gutenberg s’occupe de tout.

Et si on jette un œil au HTML généré par l’éditeur, c’est ultra simple :

HTML

C’est tout pour les bases ! Qui aurait cru que ça allait être aussi simple de pouvoir insérer des blocs enfants dans notre bloc custom ?

Avec ça, libre à vous de faire des blocs plus évolués, des éléments de structures comme des sections, colonnes… Avec les blocs imbriqués, vous n’avez plus de limites !

Idée

Vous pourriez créer un bloc sur-mesure pour gérer toute l’en-tête de votre site, et utiliser InnerBlocks à l’endroit du menu pour le laisser administrable.

Attention

Vous ne pouvez insérer qu’une seule zone de blocs enfants par bloc !

Limiter les blocs autorisés

On va maintenant pousser la configuration un peu plus loin grâce à quelques réglages de ce composant.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Et le premier que je veux vous montrer va nous permettre de limiter les blocs autorisés à l’intérieur de notre conteneur.

JSX
/src/08-innerblocks/edit.js

Ici, j’ai simplement défini les blocs autorisés dans une constante ALLOWED_BLOCKS avant le return(). Pour cela j’ai utilisé les noms internes des blocs, comme core/image.

Vous pouvez connaître la liste complète des blocs et leurs noms ici :

Liste des blocs de l’éditeur

Bien sûr, ça fonctionne aussi avec vos blocs custom et ceux fournis par les extensions. Pour cela, utilisez le nom que vous avez indiqué dans la propriété name du block.json.

Du côté de l’éditeur, lorsqu’on clique sur le bouton +, seuls les blocs définis sont disponibles :

Seuls les blocs autorisés sont proposés par le menu d'insertion de blocs à l'intérieur de notre conteneur.
Seuls les blocs autorisés apparaissent

Cette option est super pratique pour garder le contrôle sur ce que pourront faire vos utilisateurs avec leurs contenus.

Définir un modèle type

Maintenant, on va voir comment préparer un contenu type qui sera automatiquement inséré avec notre conteneur.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

On va voir qu’on peut même verrouiller le template créé et n’autoriser que la modification des contenus.

JSX
/src/08-innerblocks/edit.js

Cette fois, on ajoute la propriété template dans InnerBlocks qui me permet de définir le template par défaut à afficher grâce à la constante BASE_TEMPLATE et qui est un tableau (= une liste d’éléments).

À l’intérieur, j’ai d’autres listes dans lesquelles j’indique à chaque fois en premier le nom du bloc à afficher, et ensuite j’enchaîne avec un objet qui va contenir les attributs de chaque bloc.

Dans cet exemple, j’insère d’abord une image vide (qui sera affichée en gris), puis ensuite un titre dans lequel j’indique en placeholder « Your title here » puis un paragraphe.

Conseil

Vous pouvez injecter un contenu par défaut via l’attribut content au lieu de placeholder.

Reportez-vous à la liste des blocs dans la documentation pour connaître les noms des blocs mais aussi la liste des attributs disponibles.

Voici le résultat :

Le conteneur montre 3 blocs intégrés par défaut lors de l'insertion : une image, un titre et un texte.
Le template par défaut de mon conteneur

De là, vous n’avez plus qu’à définir un contenu pour chaque bloc et sélectionner une image.

Attention

Vous ne verrez pas le template si vous avez déjà modifié le contenu du bloc. Dans ce cas, supprimez le bloc et insérez-le à nouveau.

Gérer des éléments imbriqués dans votre template

Vous pourrez également imbriquer plusieurs niveaux de blocs grâce aux groupes et aux colonnes.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

En utilisant un bloc conteneur (groupe, colonnes, bannière…), utilisez le troisième argument. Celui-ci vous permettra d’indiquer quels blocs insérer à l’intérieur.

Exemple avec des colonnes :

JSX
/src/08-innerblocks/edit.js

Faites très attention aux crochets, car comme vous pouvez le voir dans l’exemple ci-dessus, il y en a beaucoup qui s’imbriquent ensemble, et ce n’est pas très facile à lire où à manipuler !

Cela dit, ça permettrait de réaliser des templates avec de belles structures complexes pour vos blocs conteneurs.

Verrouiller le modèle

Si vous voulez forcer vos utilisateurs à respecter le modèle, vous pouvez le verrouiller contre le déplacement et la suppression très facilement. Ainsi, ils pourront uniquement éditer le contenu.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Pour cela, ajoutez simplement templatelock dans votre code, avec la valeur all :

JSX
/src/08-innerblocks/edit.js

Désormais, on ne peut plus supprimer les blocs enfants, ni les déplacer, ni en insérer d’autres. On voit d’ailleurs un cadenas dans la vue en liste :

Les blocs enfants apparaissent sur la vue en liste avec un cadenas, signe qu'on ne peut plus les déplacer ni les supprimer.
Les blocs sont verrouillés : on ne peut ni les déplacer, ni les supprimer, ni en rajouter

Comme l’indique la documentation de WordPress, il existe d’autres valeurs pour templateLock :

  • 'insert' : impossible d’insérer un nouveau bloc mais on peut déplacer ceux existants ;
  • 'all' : impossible de supprimer, déplacer ou ajouter un bloc ;
  • {false} : pas de verrouillage appliqué ;
  • 'contentOnly' : supprime toutes les options de la toolbar du bloc comme « copier les styles », « verrouiller », « copier / coller… ».

Afficher un exemple du bloc avec des enfants

Dans les autres blocs, on définissait des valeurs dans example du fichier block.json afin de générer un aperçu sympa du bloc dans le menu d’insertion des blocs. On va pouvoir en faire de même avec notre conteneur.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Pour cela, on va cette fois remplir la sous-section innerBlocks de example, au lieu de attributes avec la même structure que pour le template par défaut :

JSON
/src/08-innerblocks/block.json

Et dans l’éditeur, on aura cet aperçu :

L'éditeur affiche un aperçu du bloc au survol de son icône. On voit les blocs d'exemple que l'on a définis dans le block.json.
Aperçu de notre conteneur avec des sous-blocs d’exemple

Changer l’élément HTML avec useInnerBlocks

Enfin, il existe une méthode alternative à InnerBlocks pour afficher les blocs enfants, qui vous permettra de maîtriser le HTML généré.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Imaginons un exemple plus complexe où vous souhaitez insérer vos blocs enfants dans une div qui porte la classe children :

JSX

Vous pouvez à la place utiliser la fonction useInnerBlockProps() (à importer également depuis @wordpress/block-editor) et faire d’une pierre 2 coups :

JSX

D’ailleurs, pour en revenir à notre premier exemple, on aurait pu récupérer les blockProps dans les InnerBlockProps et simplifier au maximum :

JSX

Les approches ne changent rien au fonctionnement, à part simplifier la syntaxe de votre code, donc faite selon ce qui vous semble le plus logique !

Si vous voulez encore plus d’exemples, consultez la documentation WordPress dédiée aux nested blocks.


Pas mal non ? Pour finir avec des bases, j’aimerai laisser la parole à Florian Truchot, véritable pionner du FSE, qui utilise intensivement Gutenberg et les blocs dans ses projets depuis maintenant de nombreuses années :

Florian Truchot
Directeur technique
Formateur

Le conseil de Florian Truchot

Développeur & Formateur WordPress

Depuis l’apparition des blocs imbriqués, je les utilise dans quasiment tous mes blocs, en remplacement du composant RichText. De cette manière, j’offre la possibilité à mes clients d’insérer du texte riche même à l’intérieur de nos blocs maison.

Grâce à l’API développeurs de Gutenberg, on a pu réussir à mettre cela en place pour tous nos projets. C’est aujourd’hui un énorme gain de temps pour nous, et une satisfaction client sans précédent.

Suivez-moi sur X : @floriantruchot • Mon site : Selina

Un bloc répéteur avec un seul type d’enfant

Avant de clôturer ce cours, je vais vous montrer un exemple dans lequel on va mettre en pratique ce que l’on a vu précédemment avec <InnerBlocks />, dans le but de faire un bloc de type « répéteur » un peu comme on en a l’habitude avec ACF, mais à la mode Gutenberg.

Du RichText multiligne aux blocs parents/enfants

Dans les tous premiers temps de Gutenberg, le composant RichText possédait une propriété multiline qui permettait de créer plusieurs paragraphes, éléments de liste à partir du même bloc. Mais il a fini par être déprécié.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Et pour cause, Gutenberg allait unifier les façons de faire : dorénavant, on privilégiera l’utilisation des blocs parents et les blocs enfants.

C’est le cas de la liste à puces, si vous regardez bien :

Une liste à puce dans Gutenberg. D'après la vue en liste, chaque élément est un bloc enfant de la liste.
Les éléments de liste sont des enfants du bloc « Liste »

Chaque élément de liste est un bloc enfant du bloc « Liste ». L’avantage, c’est que chaque élément de liste pourra avoir un réglage différent des autres. Par exemple, un élément en particulier pourra avoir une couleur différente des autres.

Bien entendu, vous ne pouvez pas utiliser le bloc « Élément de liste » en dehors du bloc « Liste ».

Et lorsqu’on est dans la liste, on peut insérer uniquement des éléments de liste. Au final ce concept c’est un peu le principe des répéteurs ACF, mais en version Gutenberg !

Créer son propre bloc répéteur avec Gutenberg

On va justement voir comment faire la même chose avec un bloc sur-mesure qui présente une todo list.

verrouillé Vidéo premium

Cette vidéo est réservée aux détenteurs de l’offre premium.
Envie de nous rejoindre ?

Choisir une formule

Pour ceux qui suivent la formule premium de la formation, vous trouverez le code de cet exemple dans le dossier /src/09 repeater/ pour le parent et /src/09 item/ pour l’enfant.

Voici à quoi ressemble les deux bloc en action :

Une TODO list qui utilise le principe de bloc parent et de bloc enfant unique de Gutenberg.
Le bloc parent/enfant unique que l’on va réaliser

Le bloc parent

Commençons par le bloc parent et plus précisément par le edit.js. Ici, rien de nouveau, à part qu’on va n’autoriser qu’un seul enfant dans ALLOWED_BLOCKS, notre bloc capitainewp/item.

JSX
/src/09-repeater/edit.js

À partir du moment où l’on autorise qu’un seul bloc, Gutenberg va arrêter de nous proposer la fenêtre de suggestion de blocs. Lorsqu’on va cliquer sur le bouton +, le seul bloc enfant possible sera ajouté automatiquement.

Le bloc enfant

Maintenant passons au fichier block.json du bloc enfant. Afin de déclarer qu’il est forcément un bloc enfant d’un autre bloc, c’est dans la propriété parent que ça va se passer :

JSON
/src/09-item/block.json

La propriété parent est un tableau dans lequel on peut mettre plusieurs parents si besoin, mais en général, on n’en définit qu’un seul. Malgré tout, on doit toujours mettre les crochets […] désignant un tableau.

Bon, dernier problème ! Lorsqu’on appuie sur la touche « Entrée » sur un élément, on a un retour à la ligne à la place de créer un nouveau bloc.

C’est le fonctionnement normal du RichText mais pour le coup, il ne nous arrange pas. Ne vous inquiétez pas, on va solutionner ça dans le prochain cours sur les données et la fonction useSelect.


La possibilité d’imbriquer des blocs est très intéressante et devrait vous ouvrir de nombreuses portes. Avec ça, vous vous ouvrez encore à d’autres horizons avec les blocs sur-mesure.

Dans le prochain cours, on va aborder un concept très important de WordPress et de l’éditeur : les hooks React useSelect et useDispatch, qui vont nous permettre de récupérer des données de l’éditeur et du site, mais également de modifier à distance d’autres blocs.

8

Questions, réponses et commentaires

  1. GINESTE Bernard

    Le 23 août 2023

    Bonjour,
    Cet article est très intéressant. Mais je voudrais insérer plusieurs InnerBlocks dans un bloc. Pour cela j’ai dupliqué les séquences d’insertion dans edit.js et save.js
    Le problème, c’est que, lorsqu’on insère un block dans un des InnerBlocks, cela se répercute sur tous les autres.
    Connaissez vous la raison de ce comportement et le moyen d’y remédier ?
    Merci d’avance.

    1. Maxime BJ

      Le 23 août 2023

      Il est impossible d’avoir plusieurs InnerBlocks, c’est pour cela ! Ce n’est pas une limitation de WordPress, c’est le fonctionnement de React qui est en cause ici. Il faut alors que tu penses tes blocs autrement du coup. Comme par exemple ajouter un autre niveau de blocs (qui seraient répétables et qui auraient chacun leurs enfants).

      1. gehin nicolas

        Le 29 janvier 2025

        Bonjour, la solution pour résoudre cette limitation est de créer un bloc avec uniquement un seul `InnerBlocks` (par exemple, `mon-slug/inner-block`), ensuite on peut insérer ce block autant de fois qu’on le souhaite dans le **template** du block parent

      2. GINESTE Bernard

        Le 24 août 2023

        Merci beaucoup pour cette réponse.
        Du coup, j’ai remarqué que InnerBlocks est au pluriel, ce qui laisse entendre que ce « réceptacle » »peut contenir plusieurs blocs.
        Aussi, j’ai tenté ceci :
        après avoir défini un premier bloc dans l’InnerBlocks, j’ai déployé le menu de ce premier bloc et j’ai pu choisir ‘ajouter avant’ ou ‘ajouter après’ pour insérer un nouveau bloc, et ainsi de suite.
        C’était ce que je voulais faire et ça fonctionne !

        Je profite de cette réponse pour vous féliciter de la clarté de vos exposés, qui permettent vraiment de s’initier à la réalisation des blocs personnalisés, sans être bloqué par des insuffisances d’information.
        Et en plus vous répondez aux questions qu’on vous soumet !

        Bien cordialement

  2. Gaëtan

    Le 20 septembre 2023

    Bonjour,
    J’ai une question concernant la partie $template :

    Dans le cas où on veut insérer un bloc acf custom dans cette variable $template, comment peut-on récupérer les champs acf de ce bloc ?

    Ex : j’ai un bloc acf appelé par exemple ‘testimonial’, comprenant 2 acf fields ‘citation’ et ‘auteur’.

    Je veux inclure ce bloc dans $template, et mettre une citation et un auteur par défaut.

    Je pensais écrire comme ça :
    [ ‘acf/testimonial’, { citation: ‘Hello !’, auteur: ‘Jean-Michel’ } ],

    Mais ça ne fonctionne pas… Tu crois que c’est possible de le faire ?

    merci pour ta réponse

    1. Maxime BJ

      Le 20 septembre 2023

      Ah très bonne question ! Malheureusement je n’ai pas la réponse. C’est super mal documenté côté WP déjà ça. Mais en tous cas j’aurais fait comme tu as dit, c’est ce qui m’aurait semblé le plus judicieux. Il faut bien que citation et auteur soient les « slugs » de tes champs ACF mais je pense que tu as bien vérifié que c’était le cas. Si tu trouves la solution, je suis preneur !

      1. Gaëtan

        Le 21 septembre 2023

        La réponse ici >> https://twitter.com/BillErickson/status/1704862320751472931

        Ça fonctionne, c’est cool !

        1. Maxime BJ

          Le 22 septembre 2023

          Ok ! Il fallait viser l’identifiant du champ, et non pas son nom directement ! Bien vu !

Laisser un commentaire