MontréalCA
Marseille FR
Création de site web

Custom post types WordPress : gérer ses contenus sur mesure sans dépendre du thème ni d'ACF

Déclarer ses types de contenu dans le thème ou tout confier à ACF sont deux dépendances qui coûtent cher sur la durée. Nous développons souvent un petit plugin sur mesure pour gérer les custom post types et les champs personnalisés. Cet article explique pourquoi, ce que WordPress permet nativement, et quand ACF reste malgré tout le bon choix.


Le 12 octobre 2024, l'extension Advanced Custom Fields a changé de mains sur WordPress.org. En pleine bataille juridique entre Automattic et l'hébergeur WP Engine, propriétaire d'ACF, WordPress.org a forké l'extension et l'a remplacée dans son annuaire officiel par une copie baptisée Secure Custom Fields. Du jour au lendemain, les sites qui installaient ou mettaient à jour la version gratuite d'ACF depuis WordPress.org recevaient en réalité une autre extension, maintenue par d'autres mains. Les développeurs qui pilotaient ces sites ont redécouvert ce jour-là une chose qu'ils savaient en théorie mais avaient rangée dans un coin : la brique sur laquelle reposait la structure de leurs contenus ne leur appartenait pas.

L'épisode s'est en partie dénoué quelques semaines plus tard, quand une décision de justice a rendu à WP Engine le contrôle de son extension. Mais il a laissé une question ouverte, celle qui nous intéresse ici. Quand vous confiez la définition de vos types de contenu et de vos champs personnalisés à une extension tierce, ou même simplement à votre thème, vous acceptez une dépendance. Sur un site de blog, l'enjeu est faible. Sur le site d'une entreprise, d'un e-commerce ou d'un organisme dont l'activité passe par le web, cette dépendance devient un risque qui se paie plus tard, au pire moment.

Chez MtoM Création, nous construisons l'essentiel de nos sites avec WordPress et Bricks Builder, et nous prenons régulièrement une décision qui surprend : pour gérer les contenus structurés d'un projet, custom post types et champs associés, nous développons un petit plugin sur mesure plutôt que de tout confier à ACF ou de déclarer ces contenus dans le thème. Nous détaillons ici les raisons de ce choix, ce que WordPress permet nativement aujourd'hui, la marche à suivre concrète avec du code propre, et les cas où ACF reste le meilleur choix. Parce que ce n'est pas une position dogmatique, c'est un arbitrage.

Custom post types et champs personnalisés, remis à plat

Avant d'entrer dans l'arbitrage, un rappel rapide pour que la suite parle à tout le monde, du chef de projet au développeur.

WordPress ne connaît pas que les articles et les pages. Il permet de déclarer ses propres types de contenu, appelés custom post types (types de publication personnalisés). Un cabinet d'avocats peut vouloir un type « Membre de l'équipe », un organisme culturel un type « Événement », un e-commerçant un type « Étude de cas ». Chacun de ces types apparaît dans le menu d'administration comme le feraient les articles, avec sa propre liste, ses propres écrans d'édition et ses propres règles d'affichage sur le site public.

Un type de contenu seul ne suffit pas. Un « Membre de l'équipe » a une photo, un poste, un numéro de téléphone, un lien LinkedIn, une biographie courte. Ces informations sont des champs personnalisés, stockés dans WordPress sous forme de post meta, c'est-à-dire de métadonnées attachées à chaque publication. La question qui structure tout le reste est simple : où et comment déclarer ces types de contenu et ces champs ? Trois réponses coexistent, et elles n'ont pas les mêmes conséquences à cinq ans. Ces types et ces champs peuvent vivre dans le thème, être confiés entièrement à une extension comme ACF, ou isolés dans un plugin dédié. Le reste de cet article compare ces trois voies et défend la troisième pour les projets qui comptent.

L'erreur qui coûte le plus cher : déclarer ses contenus dans le thème

C'est le réflexe le plus répandu, y compris chez des intégrateurs expérimentés. Le geste consiste à ouvrir le fichier functions.php du thème, y coller un register_post_type, ajouter quelques champs, et le site fonctionne. Le problème n'apparaît pas le jour de la mise en ligne. Il apparaît le jour où il faut changer de thème.

Souvenez-vous de la façon dont WordPress sépare les responsabilités. Le thème est censé gérer l'apparence, la présentation, la mise en forme. Les données, elles, appartiennent au site. Quand vous déclarez un custom post type dans le thème, vous mariez les deux. Le jour où une refonte impose un nouveau thème, ou même une simple bascule vers un constructeur différent, la déclaration disparaît avec l'ancien thème. Les publications, elles, restent en base de données, mais WordPress ne sait plus qu'elles existent : le type de contenu n'est plus enregistré, les entrées deviennent orphelines, le menu d'administration correspondant s'évapore. Le contenu est toujours là, techniquement, mais plus personne ne peut le voir ni le modifier depuis le back-office.

Ce scénario est classique lors d'une reprise de site existant. Un client souhaite refaire le design de son site, et l'audit technique révèle que tout son catalogue de réalisations, ses fiches produits ou son annuaire de membres est câblé dans le thème sortant. La refonte, qui devait être un travail de présentation, se transforme alors en opération de sauvetage de données. Ce n'est pas une fatalité technique, c'est la conséquence directe d'un choix d'architecture pris au début, souvent sans y penser.

La règle que nous appliquons est donc nette : ce qui relève de la donnée ne vit jamais dans le thème. Un custom post type, ses champs, ses règles de réécriture d'URL doivent survivre à un changement de thème. Le seul endroit qui garantit cette survie, c'est une extension. Reste à savoir laquelle.

Pourquoi un plugin sur mesure plutôt qu'ACF

ACF est un excellent outil, et nous ne le contestons pas. Il a rendu la gestion des champs personnalisés accessible à une génération entière d'intégrateurs, et pour beaucoup de projets il reste parfaitement adapté. Mais sur les projets où la structure de contenu est le cœur du site, nous préférons souvent écrire nous-mêmes un petit plugin. Trois raisons pèsent le plus dans cet arbitrage.

La dépendance externe, un risque qui se matérialise vraiment

L'épisode d'octobre 2024 n'est pas une hypothèse d'école, c'est un fait daté. Une extension propriétaire est un tiers dans votre architecture, avec sa propre gouvernance, ses propres conditions de licence, sa propre feuille de route et, désormais, sa propre exposition à des conflits qui vous dépassent. ACF appartient à WP Engine depuis 2022, après son rachat à Delicious Brains. Quand ce propriétaire se retrouve au centre d'un litige avec la fondation qui contrôle le canal de distribution officiel, ce sont les sites qui en dépendent qui absorbent l'incertitude.

Un plugin sur mesure supprime purement et simplement ce tiers. Le code qui déclare vos types de contenu, vous le possédez, vous le versionnez, vous le déployez. Personne ne peut décider un matin de le forker, de modifier ses conditions d'utilisation ou d'interrompre ses mises à jour. Pour un organisme ou une entreprise dont le site est un outil de travail quotidien, retirer une dépendance externe critique n'est pas un caprice de développeur, c'est de la gestion de risque élémentaire.

Le coût de licence, discret mais récurrent

ACF Pro, la version qui débloque les champs les plus utiles comme les champs répétables, les groupes flexibles et les blocs, démarre autour de 49 dollars américains par an et par licence, avec des paliers pour plusieurs sites. Sur un site isolé, la somme est modeste. À l'échelle d'une agence qui maintient un parc de sites, ou d'une entreprise qui multiplie ses plateformes, c'est une ligne récurrente qui s'additionne, se renouvelle et qu'il faut suivre. Le jour où une licence expire sans que personne l'ait remarqué, ce sont les mises à jour de sécurité de l'extension qui s'arrêtent, ce qui nous ramène directement au sujet des vulnérabilités non patchées.

Un plugin maison n'a pas de licence à renouveler. Le coût est concentré au départ, dans le temps de développement, et il ne se reproduit pas chaque année. Pour un projet où la structure de contenu est stable, cet investissement initial est vite amorti face à un abonnement qui court indéfiniment. Ce raisonnement ne vaut évidemment pas pour tous les projets, et nous y reviendrons, mais il est réel et il se chiffre.

Le contrôle et la sobriété technique

ACF est une extension généraliste. Pour couvrir tous les cas d'usage de ses millions d'installations actives, elle embarque une interface d'administration complète, un moteur de rendu de champs, un système de localisation, une couche de compatibilité avec le REST et l'éditeur de blocs. Tout cela s'exécute à chaque chargement de page d'administration, et parfois côté public. Sur un site qui n'utilise que trois types de champs simples, c'est beaucoup de code pour peu de besoin.

Un plugin sur mesure ne contient que ce dont votre projet a besoin, ni plus ni moins. Vous déclarez exactement les types de contenu utiles, exactement les champs utiles, avec exactement les règles de validation utiles. Le résultat est plus léger, plus rapide à auditer, plus facile à faire évoluer, et il ne vous expose qu'à la surface d'attaque que vous avez écrite vous-même. Cette sobriété rejoint une conviction qui traverse notre façon de travailler : un site tient mieux dans la durée quand chacune de ses briques est comprise et maîtrisée par ceux qui l'entretiennent, plutôt qu'empilée par commodité.

Ce que ça donne dans le code

La bonne nouvelle, c'est que WordPress fournit tout ce qu'il faut nativement. Déclarer un custom post type et ses champs sans aucune extension tient en quelques dizaines de lignes bien organisées. Voici la structure d'un plugin minimal, réduite à l'essentiel pour rester lisible.

Un plugin, c'est d'abord un fichier avec un en-tête reconnu par WordPress, placé dans son propre dossier sous wp-content/plugins/.

<?php
/**
 * Plugin Name: MtoM Création, types de contenu
 * Description: Déclare les custom post types et champs du site.
 * Version:     1.0.0
 * Author:      MtoM Création
 */

// Empêche l'accès direct au fichier.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

L'enregistrement d'un type de contenu se fait sur le hook init, jamais ailleurs. C'est ce qui garantit que le type existe à chaque requête, dans l'administration comme sur le site public.

add_action( 'init', 'mtom_register_membre_cpt' );

function mtom_register_membre_cpt() {
    register_post_type( 'membre', array(
        'labels' => array(
            'name'          => 'Membres de l’équipe',
            'singular_name' => 'Membre',
            'add_new_item'  => 'Ajouter un membre',
        ),
        'public'       => true,
        'has_archive'  => true,
        'menu_icon'    => 'dashicons-groups',
        'rewrite'      => array( 'slug' => 'equipe' ),
        'supports'     => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'show_in_rest' => true, // Indispensable pour l'éditeur de blocs.
    ) );
}

En moins de vingt lignes, le type « Membre » existe, il a sa propre entrée de menu, ses archives, une URL propre en /equipe/, et il est disponible dans l'éditeur de blocs grâce à show_in_rest. Aucune extension n'a été installée.

Déclarer les champs personnalisés proprement

Pour les champs, WordPress fournit register_post_meta. Cette fonction déclare une métadonnée, son type, et surtout l'expose au REST et à l'éditeur de blocs, ce qui sera déterminant pour l'affichage. Chaque champ est déclaré une fois, avec sa validation.

add_action( 'init', 'mtom_register_membre_meta' );

function mtom_register_membre_meta() {
    register_post_meta( 'membre', 'poste', array(
        'type'              => 'string',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_text_field',
        'auth_callback'     => function () {
            return current_user_can( 'edit_posts' );
        },
    ) );

    register_post_meta( 'membre', 'telephone', array(
        'type'              => 'string',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_text_field',
    ) );
}

Le point important ici est le sanitize_callback. C'est lui qui nettoie la donnée avant enregistrement, votre première ligne de défense contre les saisies malformées ou malveillantes. Avec ACF, ce nettoyage est géré pour vous, ce qui est confortable mais opaque. En l'écrivant vous-même, vous savez exactement ce qui entre en base et vous l'assumez.

Une méta-boîte de saisie sans dépendance

Reste à offrir à l'équipe éditoriale une interface pour saisir ces champs. Deux options existent. La première, la plus classique, est une méta-boîte affichée sous l'éditeur, en quelques lignes de PHP.

add_action( 'add_meta_boxes', 'mtom_membre_metabox' );

function mtom_membre_metabox() {
    add_meta_box(
        'mtom_membre_infos',
        'Informations du membre',
        'mtom_membre_metabox_render',
        'membre',
        'side'
    );
}

function mtom_membre_metabox_render( $post ) {
    wp_nonce_field( 'mtom_membre_save', 'mtom_membre_nonce' );
    $poste = get_post_meta( $post->ID, 'poste', true );
    printf(
        '<label>Poste<br><input type="text" name="poste" value="%s" class="widefat"></label>',
        esc_attr( $poste )
    );
}

add_action( 'save_post_membre', 'mtom_membre_save' );

function mtom_membre_save( $post_id ) {
    if ( ! isset( $_POST['mtom_membre_nonce'] )
        || ! wp_verify_nonce( $_POST['mtom_membre_nonce'], 'mtom_membre_save' ) ) {
        return;
    }
    if ( isset( $_POST['poste'] ) ) {
        update_post_meta( $post_id, 'poste', sanitize_text_field( $_POST['poste'] ) );
    }
}

Le wp_nonce_field et sa vérification ne sont pas décoratifs. Ils protègent l'enregistrement contre les requêtes falsifiées, un point que toute extension sérieuse gère et que votre code doit gérer aussi. C'est précisément le genre de détail qui distingue un plugin maison bien écrit d'un bricolage collé dans le thème.

Rendre la liste d'administration lisible

Un dernier raffinement qui change tout pour l'équipe qui utilise le back-office au quotidien : ajouter des colonnes personnalisées à la liste des publications. Par défaut, la liste des membres n'affiche que le titre et la date. Deux filtres suffisent à y montrer le poste.

add_filter( 'manage_membre_posts_columns', function ( $columns ) {
    $columns['poste'] = 'Poste';
    return $columns;
} );

add_action( 'manage_membre_posts_custom_column', function ( $column, $post_id ) {
    if ( 'poste' === $column ) {
        echo esc_html( get_post_meta( $post_id, 'poste', true ) );
    }
}, 10, 2 );

Ce sont ces détails d'ergonomie, invisibles sur les maquettes, qui font qu'une équipe s'approprie réellement son site ou au contraire finit par redouter son propre back-office.

Relier les champs à l'affichage sans écrire de PHP dans le thème

Il reste une étape que beaucoup associent encore obligatoirement à ACF ou à du code dans les templates : afficher la valeur d'un champ personnalisé à l'endroit voulu sur la page. Depuis WordPress 6.5, sorti en 2024, l'API Block Bindings répond à ce besoin nativement. Elle permet de relier un attribut d'un bloc, le texte d'un paragraphe, la source d'une image, l'URL d'un bouton, à une source de données, dont les champs personnalisés font partie.

Concrètement, un bloc de paragraphe peut être connecté à la méta poste directement dans le balisage du bloc, sans passer par le PHP du thème.

<!-- wp:paragraph {
    "metadata":{
        "bindings":{
            "content":{
                "source":"core/post-meta",
                "args":{"key":"poste"}
            }
        }
    }
} -->
<p></p>
<!-- /wp:paragraph -->

Depuis WordPress 6.7, publié en octobre 2024, cette liaison se fait même sans toucher au code : un panneau « Attributs » dans l'éditeur permet de connecter un bloc de titre, de paragraphe, de bouton ou d'image à un champ personnalisé via une simple liste déroulante, à condition que la méta ait été enregistrée avec show_in_rest. WordPress 7.0 a étendu ce mécanisme aux blocs personnalisés grâce au filtre block_bindings_supported_attributes, et gère désormais le formatage localisé des nombres et des dates. Autrement dit, la brique qui manquait autrefois à une gestion de champs entièrement native existe désormais, elle est maintenue par le cœur de WordPress, et elle ne dépend d'aucune extension.

Cette évolution est importante pour notre arbitrage. L'un des vrais arguments historiques en faveur d'ACF était la facilité avec laquelle il affichait les champs dans les templates. Cet argument s'est considérablement affaibli à mesure que le cœur de WordPress a rattrapé le besoin. Ce qui exigeait hier une extension et du code de template se fait aujourd'hui avec des outils fournis en standard.

Quand ACF reste le bon choix, sans hésiter

Défendre le plugin sur mesure ne veut pas dire condamner ACF. Ce serait malhonnête, et surtout faux. Il y a des projets où nous recommandons ACF sans la moindre réserve, et savoir les reconnaître fait partie du métier.

Le premier cas est celui du délai. Quand un projet doit sortir vite et que la structure de contenu est classique, ACF fait gagner un temps réel. Construire une interface de saisie riche, avec des champs répétables ou des groupes conditionnels, prend plus de temps à la main qu'avec les outils visuels d'ACF. Sur un planning serré, ce temps a une valeur, et l'abonnement se justifie largement.

Le deuxième cas est celui de la maintenance par des personnes qui ne codent pas. Si le site sera repris par une équipe interne non technique qui devra elle-même faire évoluer les champs, ajouter un type de contenu, modifier un formulaire de saisie, l'interface graphique d'ACF est un vrai atout. Un plugin sur mesure impose de repasser par un développeur à chaque évolution de structure, ce qui n'est pas toujours souhaitable ni économique.

Le troisième cas est celui du prototypage et des structures très mouvantes. Quand le modèle de contenu n'est pas encore stabilisé et qu'il va changer plusieurs fois avant de se figer, l'agilité d'ACF évite de réécrire du code à chaque itération. Une fois la structure stabilisée, la question de la migration vers du natif peut éventuellement se reposer, mais pas avant.

La ligne de partage est donc assez claire. Plus la structure de contenu est stable, centrale et destinée à durer, plus le plugin sur mesure prend l'avantage. Plus elle est mouvante, secondaire ou maintenue par des non-développeurs, plus ACF garde le sien. Ce n'est pas une guerre de chapelle, c'est une question de contexte, et un bon prestataire vous dira honnêtement dans lequel vous vous trouvez.

Le cas RT21 : dissocier proprement les responsabilités

Ce raisonnement n'est pas théorique, nous l'appliquons sur nos projets. Lors de la refonte du site du Regroupement pour la Trisomie 21, un organisme communautaire montréalais, mise en ligne en juin 2026, nous sommes intervenus comme atelier technique pour l'agence La Clique, qui portait la direction créative et les maquettes. Notre périmètre couvrait toute l'exécution WordPress, et parmi les décisions d'architecture, celle des contenus structurés méritait d'être posée.

Plutôt que de faire reposer les types de contenu de l'organisme sur le thème ou sur une extension payante, nous avons développé un plugin WordPress sur mesure dédié aux custom post types. La logique était exactement celle décrite plus haut : le thème gère l'affichage, le plugin gère les données. Pour un organisme dont le site est un vrai point de contact, où un parent cherche un programme de jour ou une place de répit, cette séparation a une conséquence concrète. Le contenu ne dépend plus d'un thème qu'une future refonte pourrait remplacer, ni d'une licence tierce à renouveler chaque année, et l'équipe garde une gestion simplifiée de ses types de contenu depuis un back-office qu'elle maîtrise.

Pour un organisme comme pour l'agence qui l'accompagne, c'est une dépendance externe de moins et un risque de moins sur la durée. C'est aussi ce que nous entendons par valeur ajoutée : au-delà du code, un choix d'architecture qui protège le client d'un problème qu'il n'aura jamais à vivre parce qu'il a été évité au bon moment.

Le contenu vous appartient, le code aussi

Le fil de cet article tient en une idée : la structure de vos contenus est trop importante pour vivre dans votre thème ou pour dépendre entièrement d'un tiers. Déclarer ses custom post types et ses champs dans un plugin, sur mesure quand le projet le justifie, c'est garantir qu'ils survivront à une refonte, qu'ils ne s'arrêteront pas avec une licence expirée, et qu'ils n'absorberont pas les turbulences d'un conflit qui ne vous concerne pas. WordPress fournit aujourd'hui tout le nécessaire pour le faire nativement, de register_post_type à l'API Block Bindings, sans la moindre extension.

Cela ne fait pas d'ACF un mauvais outil, et nous continuons de le recommander quand le contexte s'y prête : délai serré, maintenance par des non-développeurs, structure encore mouvante. La vraie compétence n'est pas de choisir un camp, c'est de savoir dans quel cas vous vous trouvez et de faire un choix défendable, qui tiendra dans cinq ans autant qu'au jour de la mise en ligne.

Si vous préparez une refonte et que vous vous demandez sur quoi reposent réellement vos contenus, ou si vous êtes une agence de création qui cherche un atelier WordPress pour exécuter proprement ce genre d'architecture, c'est exactement le type de décision que nous aimons prendre en amont, avant qu'elle ne devienne un problème.

Publié le 03 juillet 2026 · Par L'équipe MtoM

Questions fréquentes

Oui, la voie native passe par du PHP, quelques dizaines de lignes bien organisées dans un plugin. Ce n'est pas hors de portée d'un intégrateur à l'aise avec le code, mais si personne dans l'équipe ne code, une extension comme ACF avec son interface graphique reste plus adaptée. Le choix dépend donc autant des compétences disponibles que du projet lui-même.

Si vos types de contenu étaient déclarés dans le thème, un changement de thème les rend invisibles dans l'administration, même si les données restent en base. Avec ACF désactivé, les valeurs des champs subsistent en base mais ne sont plus affichées ni éditables via l'interface de l'extension. Placer cette logique dans un plugin dédié protège justement les contenus de ces deux scénarios.

Pour relier un champ personnalisé à un bloc de titre, de paragraphe, de bouton ou d'image, oui, l'API Block Bindings le fait nativement depuis WordPress 6.5, avec une interface d'édition depuis la version 6.7. Pour des mises en page de champs très complexes ou des champs répétables imbriqués, ACF garde des fonctionnalités qui demanderaient davantage de travail à reproduire à la main.

Le coût d'un plugin sur mesure est concentré au départ, dans le temps de développement, et ne se répète pas. ACF Pro est un abonnement récurrent, autour de 49 dollars américains par an et par licence à l'entrée. Sur une structure de contenu stable et durable, le plugin est vite plus économique ; sur un projet ponctuel ou très évolutif, l'abonnement peut rester plus avantageux.

Parce que le thème est censé gérer l'apparence, pas les données. Un custom post type déclaré dans le thème disparaît de l'administration dès que vous changez de thème, ce qui transforme une simple refonte de design en opération de récupération de contenu. Une extension, même minimale, garantit que vos types de contenu survivent aux évolutions de présentation.

Oui. Les custom post types et champs déclarés nativement ou dans un plugin sur mesure sont indépendants du constructeur de page. Bricks Builder comme Elementor savent afficher ces contenus, et le fait de les avoir isolés dans un plugin rend justement plus simple un futur changement de constructeur, puisque la donnée ne dépend d'aucun des deux.

Vous avez un projet web?
Travaillons ensemble !

Nous sommes là pour vous accompagner dans la mise en place de votre projet web, de la première idée jusqu'à sa réalisation complète.