Une agence de voyages m’a demandé d’ajouter un champ « code fournisseur interne » sur ses fiches de séjours, un Custom Post Type maison nommé sejour. Rien de spectaculaire, sauf que le site venait tout juste de migrer vers l’éditeur de blocs, et que la première version de ma métabox refusait obstinément de s’afficher dans la nouvelle interface.
Voici comment déclarer, sauvegarder et rendre compatible avec Gutenberg une métabox classique, sans dupliquer le code entre les deux éditeurs.
Déclarer la métabox
Tout commence par le hook add_meta_boxes, qui s’exécute après que WordPress a déterminé l’écran d’édition courant. On y appelle add_meta_box() en précisant l’écran cible, ici le CPT sejour.
add_action( 'add_meta_boxes', 'voyages_ajouter_metabox_fournisseur' );
function voyages_ajouter_metabox_fournisseur() {
add_meta_box(
'voyages_fournisseur',
'Fournisseur',
'voyages_afficher_metabox_fournisseur',
'sejour',
'side',
'default'
);
}
function voyages_afficher_metabox_fournisseur( $post ) {
wp_nonce_field( 'voyages_fournisseur_nonce', 'voyages_fournisseur_nonce_field' );
$code = get_post_meta( $post->ID, '_voyages_code_fournisseur', true );
echo '<label for="voyages_code_fournisseur">Code fournisseur</label>';
echo '<input type="text" id="voyages_code_fournisseur" name="voyages_code_fournisseur" value="' . esc_attr( $code ) . '" class="widefat" />';
}
Le champ $screen (ici 'sejour') peut aussi être un tableau d’écrans, ou une chaîne générique comme 'post' pour cibler tous les types standards. La position ('side', 'normal', 'advanced') et la priorité influencent seulement l’ordre d’affichage, jamais le comportement.
Sauvegarder les champs sans casser l’autosave
La sauvegarde se fait sur le hook save_post, mais ce hook se déclenche dans des contextes qu’il faut savoir écarter : une sauvegarde automatique, une révision, ou une requête sans les droits suffisants.

add_action( 'save_post_sejour', 'voyages_sauvegarder_metabox_fournisseur' );
function voyages_sauvegarder_metabox_fournisseur( $post_id ) {
if ( ! isset( $_POST['voyages_fournisseur_nonce_field'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_POST['voyages_fournisseur_nonce_field'], 'voyages_fournisseur_nonce' ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['voyages_code_fournisseur'] ) ) {
update_post_meta(
$post_id,
'_voyages_code_fournisseur',
sanitize_text_field( wp_unslash( $_POST['voyages_code_fournisseur'] ) )
);
}
}
Utiliser save_post_sejour plutôt que le générique save_post évite d’exécuter la fonction pour chaque article ou page du site, un gain de performance qui passe facilement inaperçu sur un site à faible trafic mais qui compte sur un catalogue de plusieurs milliers de fiches.
Pourquoi la métabox disparaissait sous Gutenberg
Le vrai problème, dans mon cas, ne venait pas de la métabox elle-même : elle s’affichait bien, mais dans un panneau repliable en bas de l’écran, loin d’où l’équipe éditoriale s’attendait à la trouver. C’est le comportement normal de l’éditeur de blocs pour les métaboxes classiques : elles sont automatiquement déplacées dans une zone dédiée nommée « Options de l’écran ».
Deux options s’offraient à moi : accepter ce placement, ou migrer le champ vers un panneau natif du PluginDocumentSettingPanel côté JavaScript. Pour un champ aussi simple, la première option restait la plus rentable en temps de développement.
- Une métabox classique continue de fonctionner sous Gutenberg sans modification, mais change de position visuelle
- Le paramètre
'__back_compat_meta_box'peut forcer son affichage dans la colonne latérale plutôt qu’en bas de page - Pour un champ destiné à apparaître dans l’inspecteur natif de blocs, il faut passer par la Block Editor API en JavaScript, une autre approche
Rendre le champ disponible dans l’API REST si besoin
Si ce champ doit un jour être lu ou modifié par une application externe ou par un bloc dynamique, il faut le déclarer explicitement avec register_post_meta() en précisant show_in_rest :
add_action( 'init', function () {
register_post_meta( 'sejour', '_voyages_code_fournisseur', array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function () {
return current_user_can( 'edit_posts' );
},
) );
} );
Sans cette déclaration, le champ reste invisible pour l’API REST, même s’il existe et se sauvegarde correctement via la métabox classique. C’est un détail facile à oublier tant que personne ne réclame l’accès à la donnée depuis l’extérieur.
En résumé
Une métabox classique reste parfaitement légitime en 2020, y compris sur un site utilisant l’éditeur de blocs : WordPress assure la compatibilité descendante sans qu’il soit nécessaire de tout réécrire en JavaScript. Le vrai travail consiste à sécuriser correctement la sauvegarde — nonce, autosave, capacité — et à décider, au cas par cas, si le champ mérite une exposition REST.
Pour ce projet, la métabox classique a suffi : l’équipe éditoriale s’est habituée en quelques jours à la retrouver dans le panneau « Options de l’écran », et aucune réécriture en composants React n’a été nécessaire.