Il arrive qu’il vous faille un bloc que WordPress ne fournit pas, par exemple un encadré d’avis aux couleurs de la marque que vos rédacteurs peuvent glisser dans n’importe quel article. Vous pouvez le bâtir sous forme de petite extension en moins d’une heure. L’outil officiel de génération, @wordpress/create-block, écrit toute l’extension pour vous : un fichier block.json qui décrit le bloc, le JavaScript de l’éditeur, les styles et le PHP qui l’enregistre. Votre travail consiste à comprendre le rôle de chaque fichier, puis à en modifier trois. Ci-dessous, vous générez un bloc appelé Notice Box et vous lui donnez un message et un réglage de ton. Vous le compilez, puis vous prouvez que WordPress l’enregistre et l’affiche sur une vraie page.
Ce que vous allez accomplir
- Générer une extension de bloc fonctionnelle avec une seule commande
npxet lire chaque fichier qu’elle crée. - Ajouter deux attributs à
block.json, c’est-à-dire les réglages enregistrés d’un bloc, et les relier à l’éditeur. - Compiler le JavaScript et le CSS, activer l’extension avec WP-CLI et confirmer que le bloc est enregistré.
- Vérifier le balisage que l’éditeur enregistre, pour savoir ce qui se retrouve dans la base de données.
- Savoir quand un bloc dynamique, rendu par PHP à chaque affichage de page, est le meilleur choix.
Prérequis
- WordPress 6.8 ou plus récent sur un site local ou de préproduction. La génération actuelle (version 5.0 de
@wordpress/create-block, publiée en octobre 2026) enregistre les blocs avec une fonction ajoutée dans 6.8. L’étape 3 montre les deux modifications pour les versions 6.5 à 6.7. - Node.js 22.22.2 ou plus récent (ou 24.15 ou plus récent), et npm, sur la machine où vous compilez. Vérifiez avec
node --version. Les outils de compilation que la génération installe,@wordpress/scripts37, exigent ce minimum, et npm avertit avecEBADENGINEsur les versions plus anciennes. - WP-CLI sur l’hôte WordPress, pour l’activation et les vérifications.
- Un accès en écriture à
wp-content/plugins/. Lancer la génération dans ce dossier sur un site local place l’extension au bon endroit dès qu’elle se termine. - Une aisance à lire du JavaScript moderne. Le côté éditeur d’un bloc est un composant React écrit en JSX (des balises semblables à du HTML à l’intérieur du JavaScript), mais vous ne modifierez que les parties montrées ici.
Les serveurs de production ont rarement Node, et ils ne devraient pas en avoir besoin. Vous compilez sur votre propre machine ou en intégration continue, puis vous livrez l’extension compilée.
Comment ça marche
Un bloc a deux vies. Dans l’éditeur, WordPress charge votre JavaScript, qui dessine le bloc et permet de le modifier. Quand l’article est enregistré, la fonction save du bloc transforme ses attributs en HTML simple, et ce HTML va dans post_content, entouré d’un commentaire qui nomme le bloc. Sur le site public, aucun JavaScript de bloc ne s’exécute pour un bloc statique comme celui-ci : WordPress affiche le HTML enregistré et charge votre feuille de style.
block.json est la source de vérité unique entre ces deux vies. PHP le lit pour enregistrer le bloc et ses fichiers. JavaScript l’importe pour que l’éditeur utilise le même nom et les mêmes attributs. L’étape de compilation, wp-scripts build, transforme src/ en build/, copie block.json au passage et écrit build/blocks-manifest.php, un tableau PHP des métadonnées de chaque bloc qui évite à WordPress de lire du JSON à chaque requête.
Le fichier d’actifs compte plus qu’il n’en a l’air. index.asset.php liste les paquets de scripts WordPress que votre code importe, comme wp-blocks et wp-block-editor, ainsi qu’une empreinte de version. WordPress s’en sert pour charger ces dépendances en premier et pour forcer le fureteur à reprendre vos fichiers quand votre code change. C’est pourquoi vous n’ajoutez jamais les scripts d’un bloc à la main.
Étape par étape
1. Générez l’extension. Depuis le dossier des extensions de votre site local, lancez l’outil avec un identifiant (le slug) et un espace de noms. L’espace de noms est le préfixe du nom du bloc (acme/notice-box) ; choisissez-en un qui vous est propre, car deux extensions ne peuvent pas enregistrer le même nom.
cd wp-content/plugins
npx @wordpress/create-block@latest notice-box \
--namespace=acme \
--title="Notice Box" \
--short-description="A highlighted notice with an info or warning tone."
Fournir un slug saute les questions interactives et applique les valeurs par défaut, dont la variante statique. npx demande une seule fois avant de télécharger l’outil ; répondez y. L’exécution prend d’une à trois minutes, car elle installe @wordpress/scripts et lance une première compilation.
Exemple de sortie (abrégée) :
Need to install the following packages:
@wordpress/create-block@5.0.0
Ok to proceed? (y) y
Creating a new WordPress plugin in the /home/you/site/wp-content/plugins/notice-box directory.
Creating a "block.json" file.
Creating a "package.json" file.
Installing `@wordpress/scripts` package. It might take a couple of minutes...
Formatting JavaScript files.
Compiling block and generating blocks manifest.
Done: WordPress plugin Notice Box bootstrapped in the /home/you/site/wp-content/plugins/notice-box directory.
You can run several commands inside:
...
Code is Poetry
Si la ligne Done: manque, remontez jusqu’à la première ligne en rouge. Une version de Node trop ancienne s’annonce par Minimum system requirements not met! tout au début.
2. Lisez ce qui a été généré. Listez les fichiers en excluant node_modules :
cd notice-box
find . -path ./node_modules -prune -o -type f -print | sort
Exemple de sortie :
./.editorconfig
./.gitignore
./build/blocks-manifest.php
./build/notice-box/block.json
./build/notice-box/index-rtl.css
./build/notice-box/index.asset.php
./build/notice-box/index.css
./build/notice-box/index.js
./build/notice-box/style-index-rtl.css
./build/notice-box/style-index.css
./build/notice-box/view.asset.php
./build/notice-box/view.js
./notice-box.php
./package-lock.json
./package.json
./readme.txt
./src/notice-box/block.json
./src/notice-box/edit.js
./src/notice-box/editor.scss
./src/notice-box/index.js
./src/notice-box/save.js
./src/notice-box/style.scss
./src/notice-box/view.js
Vous ne modifiez que le contenu de src/ et, rarement, le fichier PHP principal. Tout ce qui se trouve dans build/ est régénéré à chaque compilation : vos modifications y seraient perdues.
| Fichier | Son rôle |
|---|---|
notice-box.php | En-tête de l’extension et un crochet init qui enregistre chaque bloc listé dans le manifeste |
src/notice-box/block.json | Nom, titre, catégorie, icône, attributs, fonctions prises en charge (supports) et chemins des fichiers |
src/notice-box/index.js | Appelle registerBlockType() avec les fonctions edit et save |
src/notice-box/edit.js | L’apparence et le comportement du bloc dans l’éditeur |
src/notice-box/save.js | Le HTML écrit dans l’article lors de l’enregistrement |
src/notice-box/style.scss | Styles du site public et de l’éditeur, compilés en style-index.css |
src/notice-box/editor.scss | Styles propres à l’éditeur, compilés en index.css |
src/notice-box/view.js | JavaScript du site public ; un exemple qui écrit dans la console |
package.json | Scripts npm : start, build, format, lint:css, lint:js, plugin-zip, packages-update |
Les fichiers -rtl.css sont des copies de droite à gauche que la compilation produit pour des langues comme l’arabe et l’hébreu. WordPress les choisit automatiquement.
3. Adaptez l’extension à votre version de WordPress. Sur WordPress 6.8 ou plus récent, sautez cette étape et laissez le code généré tel quel. La génération écrit Requires at least: 6.8 dans l’en-tête de notice-box.php : WordPress 6.5 à 6.7 refuse donc d’activer l’extension, avec un avertissement indiquant que le site ne respecte pas ses exigences minimales. Le crochet init généré appelle aussi wp_register_block_types_from_metadata_collection(), une fonction ajoutée dans 6.8. Abaissez d’abord l’en-tête :
sed -i 's/Requires at least: 6.8/Requires at least: 6.5/' notice-box.php
grep 'Requires at least' notice-box.php
* Requires at least: 6.5
Remplacez ensuite le corps de la fonction d’enregistrement par l’appel classique, qui lit directement le block.json compilé :
function acme_notice_box_block_init() {
register_block_type( __DIR__ . '/build/notice-box' );
}
add_action( 'init', 'acme_notice_box_block_init' );
4. Activez l’extension et confirmez que WordPress voit le bloc.
wp plugin activate notice-box
wp eval 'var_dump( WP_Block_Type_Registry::get_instance()->is_registered( "acme/notice-box" ) );'
Plugin 'notice-box' activated.
Success: Activated 1 of 1 plugins.
bool(true)
bool(false) signifie que le crochet init ne l’a pas enregistré. Vérifiez que build/blocks-manifest.php existe ; sinon, lancez npm run build depuis le dossier de l’extension. Un avertissement sur les exigences minimales veut dire que l’étape 3 s’applique à votre site.
5. Déclarez les attributs dans block.json. Remplacez src/notice-box/block.json par la version ci-dessous. Elle ajoute un attribut message, relu à partir de l’élément p enregistré, et un attribut tone limité à deux valeurs. Elle active aussi les réglages de marge intérieure et retire viewScript, car ce bloc n’a besoin d’aucun JavaScript sur le site public.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "acme/notice-box",
"version": "0.1.0",
"title": "Notice Box",
"category": "widgets",
"icon": "info",
"description": "A highlighted notice with an info or warning tone.",
"example": {},
"attributes": {
"message": {
"type": "string",
"source": "html",
"selector": "p"
},
"tone": {
"type": "string",
"enum": [ "info", "warning" ],
"default": "info"
}
},
"supports": {
"html": false,
"spacing": {
"padding": true
}
},
"textdomain": "notice-box",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
Supprimez ensuite le script d’exemple pour que la compilation cesse de le produire :
rm src/notice-box/view.js
La commande n’affiche rien quand elle réussit. apiVersion: 3 indique à WordPress que le bloc est prêt pour l’iframe de l’éditeur, le cadre isolé dans lequel l’éditeur dessine le contenu depuis WordPress 6.3. Gardez-le.
6. Reliez les attributs à l’éditeur et au HTML enregistré. Remplacez src/notice-box/edit.js :
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
RichText,
InspectorControls,
} from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( { attributes, setAttributes } ) {
const { message, tone } = attributes;
const blockProps = useBlockProps( { className: `is-tone-${ tone }` } );
return (
<>
<InspectorControls>
<PanelBody title={ __( 'Notice settings', 'notice-box' ) }>
<SelectControl
__next40pxDefaultSize
__nextHasNoMarginBottom
label={ __( 'Tone', 'notice-box' ) }
value={ tone }
options={ [
{ label: __( 'Info', 'notice-box' ), value: 'info' },
{ label: __( 'Warning', 'notice-box' ), value: 'warning' },
] }
onChange={ ( value ) => setAttributes( { tone: value } ) }
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
<RichText
tagName="p"
value={ message }
onChange={ ( value ) => setAttributes( { message: value } ) }
placeholder={ __( 'Write the notice…', 'notice-box' ) }
/>
</div>
</>
);
}
Remplacez src/notice-box/save.js. Il doit produire la même enveloppe et la même classe que l’éditeur, sinon l’éditeur ne reconnaîtra plus le contenu enregistré par la suite :
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { message, tone } = attributes;
return (
<div { ...useBlockProps.save( { className: `is-tone-${ tone }` } ) }>
<RichText.Content tagName="p" value={ message } />
</div>
);
}
Remplacez src/notice-box/style.scss :
.wp-block-acme-notice-box {
border-left: 4px solid #0b4d78;
background: #eef4f9;
padding: 1rem 1.25rem;
&.is-tone-warning {
border-left-color: #a06010;
background: #fdf6ec;
}
}
@wordpress/components ne figurait pas dans les dépendances de la génération. La compilation le relie à la copie que WordPress charge déjà, mais déclarez-le pour que votre éditeur de code et l’outil d’analyse (le linter) puissent le trouver :
npm install @wordpress/components
Il est en général déjà installé comme dépendance de @wordpress/block-editor : npm peut donc répondre up to date. C’est réussi s’il n’y a aucune ligne npm ERR! et si package.json liste maintenant @wordpress/components sous dependencies :
grep -o '"@wordpress/components": "[^"]*"' package.json
Exemple de sortie :
"@wordpress/components": "^43.0.0"
7. Compilez. Pour une compilation ponctuelle, utilisez npm run build. Pendant que vous travaillez, npm start recompile à chaque enregistrement.
npm run build
Exemple de sortie (dernières lignes) :
> notice-box@0.1.0 build
> wp-scripts build --blocks-manifest
webpack 5.111.0 compiled successfully in 3184 ms
Toute ligne ERROR in signifie que le fichier compilé n’a pas été écrit et que le bloc garde son ancien code. La ligne nomme le fichier et le numéro de ligne ; une balise fermante oubliée dans edit.js est la cause habituelle.
8. Insérez le bloc et lisez ce qui a été enregistré. Ouvrez un brouillon d’article et ajoutez Notice Box depuis l’outil d’insertion, dans la catégorie Widgets. Tapez un message et réglez Tone sur Warning dans la barre latérale. Enregistrez, puis ouvrez l’Éditeur de code depuis le menu Options (les trois points, en haut à droite) ou avec Ctrl+Maj+Alt+M. Vous devriez voir :
<!-- wp:acme/notice-box {"tone":"warning"} -->
<div class="wp-block-acme-notice-box is-tone-warning"><p>Maintenance window tonight at 22:00.</p></div>
<!-- /wp:acme/notice-box -->
Le commentaire porte tone, car cet attribut n’a pas de source. Le message a "source": "html" : WordPress le relit donc dans l’élément p et ne le répète pas dans le commentaire. Choisissez Info et tone disparaît du commentaire, parce qu’il vaut alors la valeur par défaut.
Valider que ça fonctionne
Créez un article en ligne de commande avec le balisage enregistré, puis récupérez-le comme un visiteur anonyme :
ID=$(wp post create --post_status=publish --post_title='Notice test' --porcelain \
--post_content='<!-- wp:acme/notice-box {"tone":"warning"} --><div class="wp-block-acme-notice-box is-tone-warning"><p>Test notice</p></div><!-- /wp:acme/notice-box -->')
curl -s "$(wp eval "echo get_permalink( $ID );")" | grep -oE 'class="wp-block-acme-notice-box[^"]*|acme-notice-box-style-(inline-)?css'
Exemple de sortie :
acme-notice-box-style-inline-css
class="wp-block-acme-notice-box is-tone-warning
Deux lignes veulent dire que c’est réussi. La première est la feuille de style que WordPress a jointe parce que le bloc est sur la page. Elle finit par -inline-css quand WordPress a écrit une petite feuille de style directement dans la page, et par -css quand il a lié le fichier. La seconde est votre HTML enregistré qui arrive jusqu’au visiteur. Si seule la seconde apparaît, la feuille de style n’est pas enregistrée ; vérifiez le champ style dans build/notice-box/block.json. Supprimez ensuite l’article de test avec wp post delete "$ID" --force.
Augmentez version dans block.json chaque fois que vous livrez une modification. WordPress l’ajoute à l’adresse de la feuille de style sous la forme ?ver=, si bien que les fureteurs téléchargent le nouveau CSS au lieu d’une copie en cache. Les scripts ne demandent rien de votre part : leur ?ver= vient de l’empreinte de index.asset.php, qui change à chaque compilation.
Pour livrer, lancez npm run plugin-zip. La commande écrit notice-box.zip avec le fichier PHP, le readme et build/, sans src/ ni node_modules, prêt à téléverser dans Extensions > Ajouter une extension.
Dépannage
Le bloc est absent de l’outil d’insertion. Faites d’abord la vérification de l’étape 4. Si elle affiche bool(true), l’éditeur garde sans doute un ancien paquet : confirmez que build/notice-box/index.js a changé après votre dernière compilation, puis rechargez l’éditeur en vidant le cache. Si elle affiche bool(false), l’extension est inactive ou build/ manque.
« Ce bloc contient un contenu inattendu ou non valide » sur des articles existants. Vous avez modifié save.js après l’enregistrement de contenu. Le HTML stocké ne correspond donc plus à ce que save produit maintenant. Ne corrigez pas les anciens articles à la main. Ajoutez une entrée deprecated qui conserve l’ancien save, comme l’explique corriger l’erreur de contenu inattendu ou non valide.
Les styles apparaissent dans l’éditeur, mais pas sur le site public. Les styles du site public doivent venir de style.scss, importé dans index.js, et aboutir dans style-index.css. Les styles de editor.scss ne quittent jamais l’éditeur. Vérifiez que block.json contient toujours "style": "file:./style-index.css".
L’éditeur devient blanc après l’activation de l’extension. Une erreur JavaScript dans votre paquet peut arrêter tout l’éditeur. Ouvrez la console du fureteur ; une erreur qui nomme notice-box/index.js désigne votre code. La liste de vérification complète se trouve dans l’éditeur de blocs s’affiche vide ou blanc.
L’extension fonctionne en local, mais la préproduction la refuse. La préproduction utilise un WordPress antérieur à 6.8 : le téléversement ou l’activation s’arrête avec un message indiquant que l’extension exige WordPress 6.8. Appliquez l’étape 3 avant de produire le zip, ou mettez WordPress à jour.
Quand cette approche est la mauvaise
Un bloc personnalisé est du code que vous entretenez à chaque version de WordPress. Si le but est un arrangement fixe de blocs natifs, comme un titre, un paragraphe et un bouton à vos couleurs, une composition (pattern) coûte moins cher et n’exige aucune compilation. Gardez le bloc pour ce que le cœur ne sait pas exprimer : attributs, réglages ou balisage sur mesure.
Choisissez la variante dynamique (--variant dynamic) quand l’affichage dépend de données au moment de la consultation, comme les derniers articles ou l’utilisateur connecté. Elle passe par render.php à chaque requête, si bien que modifier le balisage n’invalide jamais le contenu enregistré. Le prix à payer est un rendu PHP à chaque affichage de page. Pour l’historique de l’outillage et les anciens blocs à faire passer à apiVersion 3, voyez le flux de développement moderne des blocs. La référence de create-block et la référence des métadonnées block.json listent toutes les options.
Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.