Essayez sans attendre l'hébergement proposé par WordPress
-15% sur le premier mois avec le code 2025PRESS15AFF

Essayer maintenant

Migrer un plugin WordPress vers l’architecture des Blocs Interactifs sans casser la compatibilité ascendante

Votre plugin fonctionne parfaitement depuis des années, mais il traîne encore ce bon vieux jQuery pendant que WordPress mise tout sur l’Interactivity API. La question n’est plus de savoir si vous devez migrer, mais comment le faire sans provoquer une révolte chez vos utilisateurs qui dépendent de vos hooks. Refactoring progressif, wrapper de compatibilité, tests de non-régression : voici la méthode pour moderniser votre code sans tout casser au passage.

Pourquoi migrer vers l’API Interactivity : enjeux et prérequis

On en a déjà parlé en détail dans un précédent article du blog : l’API Interactivity est LA grande nouveauté pour rendre les blocs Gutenberg réellement interactifs, sans surcharge inutile de JavaScript. Je ne vais pas tout réexpliquer ici (allez lire l’article si ce n’est pas encore fait), mais avant de plonger dans la migration technique de votre plugin, il faut comprendre pourquoi ce chantier vaut le coup. Et surtout, vérifier que votre projet est prêt à l’accueillir.

Les limites du couple jQuery / vanilla JS dans les plugins historiques

Soyons honnêtes : la plupart des plugins WordPress qui datent d’avant Gutenberg (ou même des premières versions de Gutenberg) reposent sur un mélange peu académique de jQuery et de vanilla JS. Ça fonctionne, certes, mais à quel prix ?

  • Performance : chaque script jQuery chargé, c’est du poids en plus sur la page, même si l’utilisateur n’interagit jamais avec la fonctionnalité concernée.
  • Maintenance : le code devient vite un empilement de patchs successifs, avec des event listeners ajoutés au fil des versions sans réelle cohérence.
  • Duplication : sur une page avec plusieurs blocs interactifs, on se retrouve souvent avec plusieurs scripts qui font sensiblement la même chose (gérer un état, écouter un clic, mettre à jour le DOM).

Bref, ce couple a fait le job pendant des années, mais il montre clairement ses limites face aux exigences actuelles de performance et de standardisation.

Ce que l’API Interactivity change concrètement (directives, store, hydratation)

L’API Interactivity, ce n’est pas juste un nouveau nom pour faire la même chose autrement. Elle introduit une véritable standardisation : au lieu de manipuler le DOM à la main, vous déclarez des directives directement dans le HTML (data-wp-interactive, data-wp-context, etc.), et le framework se charge de la logique sous le capot.

Trois notions à avoir en tête avant de vous lancer :

  • Les directives : elles remplacent vos event listeners manuels par des attributs déclaratifs, lisibles directement dans le markup.
  • Le store : c’est l’état partagé de votre bloc, géré via wp_interactivity_state côté serveur et synchronisé côté client. Fini les variables globales qui traînent partout.
  • L’hydratation : le HTML est déjà rendu côté serveur (donc bon pour le SEO et le temps de chargement perçu), et le JS vient ensuite « réveiller » l’interactivité, sans tout recharger.

Résultat : moins de JS custom, une base commune à tous les blocs, et une meilleure compatibilité avec l’avenir de WordPress (Gutenberg continue d’évoluer, autant suivre le mouvement maintenant plutôt que de rattraper le retard dans deux ans).

Auditer son plugin avant de se lancer : checklist de compatibilité

Avant de toucher une ligne de code, prenez le temps de faire l’état des lieux. C’est cette étape qui vous évitera bien des mauvaises surprises en cours de migration.

Prérequis techniques à valider en premier :

  • WordPress 6.5 minimum recommandé pour une utilisation stable de l’API Interactivity (certaines fonctionnalités ont été affinées dans les versions suivantes, donc gardez un œil sur le changelog).
  • Une compréhension de base de wp_interactivity_state, data-wp-interactive et data-wp-context : pas besoin d’être expert, mais il faut savoir à quoi ça sert avant de refactorer.

Checklist d’audit à passer sur votre plugin existant :

  • Identifiez tout le JS déjà en place : jQuery, vanilla JS, ou même un framework tiers (Alpine.js, Vue, peu importe).
  • Repérez les endroits où vous manipulez le DOM manuellement (ajout/suppression de classes, addEventListener, sélecteurs querySelector un peu partout).
  • Listez les dépendances externes : librairies chargées via CDN, scripts tiers, autres plugins qui interagissent avec le vôtre.

Cette checklist, gardez-la sous la main. Elle va vous servir de fil conducteur pour toute la suite de la migration.

Méthodologie de migration progressive étape par étape

Maintenant qu’on sait pourquoi migrer (et qu’on a fait notre audit technique), passons au concret. On va découper la migration progressive en 4 étapes distinctes, chacune testable indépendamment. L’idée : ne jamais casser le plugin en production, quitte à cohabiter un moment avec deux systèmes qui font la même chose. Oui, c’est un peu inconfortable au début, mais c’est le prix à payer pour dormir tranquille.

Étape 1 : isoler la logique JS existante dans des modules autonomes

Avant même de toucher à l’Interactivity API, on nettoie l’existant. Concrètement : on prend son gros fichier script.js de 400 lignes avec tout mélangé (event listeners, appels AJAX, manipulation du DOM) et on le découpe en modules ES avec import/export.

Pourquoi cette étape avant tout le reste ? Parce qu’elle ne change rien au comportement du plugin (donc zéro risque de régression) et qu’elle force à clarifier les responsabilités de chaque bout de code. Un module pour la logique métier, un autre pour les interactions DOM, un autre pour les appels API. Ça paraît anodin, mais c’est souvent à ce moment qu’on réalise à quel point le code legacy est enchevêtré (spaghetti code, on connaît tous ça).

Techniquement, il faut aussi s’assurer que le build (webpack, ou @wordpress/scripts si vous l’utilisez déjà) supporte bien les modules ES. Dans la plupart des cas, c’est déjà configuré côté WordPress, donc pas de mauvaise surprise à prévoir.

Étape 2 : introduire l’API Interactivity en parallèle du code legacy

C’est ici que ça devient intéressant. On introduit l’Interactivity API sans supprimer une seule ligne du code existant. Côté PHP, on enregistre un store avec wp_interactivity_state() :

wp_interactivity_state( 'mon-plugin', array(
    'isOpen' => false) );

Côté JS, on crée le store correspondant :

import { store } from '@wordpress/interactivity';

store( 'mon-plugin', {
    state: {
        get isOpen() {
            return false;
        },
    },
    actions: {
        toggle: () => {
            // logique à venir
        },
    },
} );

Pour éviter que les deux systèmes se marchent dessus, on utilise un feature flag (une simple constante PHP ou une option en base) ou un attribut data- conditionnel qui active l’un ou l’autre selon le contexte. Par exemple, on peut afficher le markup legacy par défaut, et ne charger le markup Interactivity que si une option « mode bêta » est activée dans les réglages du plugin. Ça permet de tester en conditions réelles sans exposer tous les utilisateurs.

Étape 3 : basculer les interactions une à une avec des exemples avant/après

Une fois le squelette en place, on migre les interactions une par une. Pas tout d’un coup, vraiment une par une. Prenons l’exemple classique d’un accordéon ou d’un bouton « favoris » (deux cas très fréquents dans les plugins WordPress).

Avant (jQuery) :

$('.mon-bouton').on('click', function() {
    $(this).toggleClass('actif');
    $(this).next('.contenu').slideToggle();
});

Simple, ça marche, mais ça dépend de jQuery, ça manipule directement le DOM, et ça devient vite difficile à maintenir si on multiplie les boutons.

Après (Interactivity API) :

Le markup dans le template PHP :

<div data-wp-interactive="mon-plugin" data-wp-context='{ "isOpen": false }'>
    <button data-wp-on--click="actions.toggle" data-wp-bind--aria-expanded="context.isOpen">
        Voir plus
    </button>
    <div data-wp-bind--hidden="!context.isOpen">
        Contenu de l'accordéon
    </div>
</div>

Et la fonction correspondante dans le store :

store( 'mon-plugin', {
    actions: {
        toggle: () => {
            const context = getContext();
            context.isOpen = !context.isOpen;
        },
    },
} );

Remarquez la différence : plus de manipulation manuelle du DOM, plus de classes CSS à jongler à la main. Le contexte réactif (data-wp-context) gère l’état, et le rendu s’adapte automatiquement. C’est plus déclaratif, un peu à la manière de ce qu’on trouve dans React ou Vue (sans le poids du framework, ce qui est justement tout l’intérêt de l’API).

Pour un toggle de favoris, le principe est identique : on remplace l’appel AJAX en jQuery par une action dans le store qui met à jour le contexte, éventuellement couplée à wp_interactivity_state() pour synchroniser avec le serveur si besoin.

Étape 4 : nettoyer le code legacy une fois la bascule validée

Dernière étape, et pas la moins satisfaisante : faire le ménage. Une fois que chaque interaction a été migrée et testée (idéalement sur un environnement de staging pendant quelques semaines), on peut commencer à retirer les scripts devenus inutiles.

Concrètement, dans plugin.php, on repère les wp_enqueue_script() qui chargeaient jQuery ou le vieux fichier JS, et on les neutralise avec wp_dequeue_script() :

function mon_plugin_nettoyer_scripts() {
    wp_dequeue_script( 'mon-plugin-jquery-legacy' );
    wp_deregister_script( 'mon-plugin-jquery-legacy' );
}
add_action( 'wp_enqueue_scripts', 'mon_plugin_nettoyer_scripts', 20 );

Attention néanmoins : vérifiez qu’aucun autre plugin (ou le thème) ne dépend de ce script avant de le supprimer définitivement. Ça arrive plus souvent qu’on ne le pense, surtout avec des thèmes qui piochent allègrement dans les scripts enregistrés par d’autres plugins.

Une fois cette vérification faite, on peut alléger le fichier principal du plugin : suppression des hooks devenus obsolètes, nettoyage des dépendances dans le fichier composer.json ou package.json si jQuery n’est plus utilisé nulle part. Le résultat ? Un plugin plus léger, plus rapide à charger, et surtout beaucoup plus simple à maintenir pour la suite.

Garantir la rétrocompatibilité pendant et après la migration

Migrer vers l’Interactivity API, c’est excitant. Mais soyons honnêtes : si votre plugin est utilisé par d’autres développeurs (via des extensions, des thèmes enfants ou de simples snippets), la moindre régression peut vite tourner au cauchemar. Le principe de non-régression n’est pas une option ici, c’est une obligation. On va donc voir comment sécuriser la transition sans sacrifier l’innovation technique.

Maintenir les hooks et filtres existants pour ne pas casser les extensions tierces

Première règle, et elle est non négociable : vos add_action et add_filter publics doivent continuer à fonctionner exactement comme avant, même si l’implémentation interne change du tout au tout. Un développeur tiers qui hook sur mon_plugin_before_render ne doit rien voir de différent, que vous soyez passé en jQuery legacy ou en store Interactivity.

La technique la plus fiable : créer une couche de compatibilité (un wrapper) qui conserve la signature publique de vos fonctions, tout en redirigeant l’exécution vers la nouvelle logique interne. Concrètement :

function mon_plugin_render_widget( $atts ) {
    // Nouvelle implémentation interne (store, data-wp-*)
    $output = Mon_Plugin_Interactivity::render( $atts );

    // On conserve le filtre historique pour ne rien casser
    return apply_filters( 'mon_plugin_widget_output', $output, $atts );
}

Ainsi, le hook mon_plugin_widget_output reste disponible, peu importe ce qui se passe derrière. C’est un peu comme changer le moteur d’une voiture sans toucher au volant : l’utilisateur ne doit rien remarquer.

Gérer la coexistence des deux architectures avec un système de version flag

Pendant la période de transition (et elle peut durer plusieurs mois en production), il faut prévoir un moyen de revenir en arrière rapidement si un bug survient. La solution la plus pragmatique : un flag de version, stocké soit en option WordPress, soit en constante dans wp-config.php.

if ( get_option( 'mon_plugin_use_legacy_js', false ) ) {
    // Ancien comportement jQuery/vanilla JS
    mon_plugin_enqueue_legacy_scripts();
} else {
    // Nouvelle architecture Interactivity API
    mon_plugin_enqueue_interactivity_scripts();
}

Ce flag permet à vos utilisateurs les plus prudents (ou ceux ayant des snippets personnalisés dépendant de l’ancien JS) de désactiver temporairement la nouvelle architecture le temps que la migration soit validée en production. Néanmoins, pensez à afficher un avertissement admin clair : « vous utilisez le mode compatibilité, sera retiré en version X.X ».

Documentez systématiquement ces changements dans votre readme.txt et changelog. Une mention type « Deprecated since 3.2, will be removed in 4.0 » évite bien des tickets de support. Par exemple :

= 3.2.0 =
* Deprecated: fonction mon_plugin_legacy_render() (utilisez mon_plugin_render_widget() à la place). Sera retirée en 4.0.

Cette transparence rassure les développeurs tiers et leur laisse le temps d’adapter leurs extensions ou thèmes enfants sans mauvaise surprise.

Tester la migration avant la mise en production

Bon, on y est. Le code est refactorisé, le wrapper de compatibilité est en place, les flags de version fonctionnent. Mais avant de pousser tout ça en production, il reste une étape non négociable : tester. Et pas juste « vite fait sur mon site local ». Une vraie campagne de tests, structurée, qui couvre les usages réels de vos utilisateurs. Voici comment je m’y prends à chaque migration importante.

Tests fonctionnels manuels sur les navigateurs et thèmes populaires

L’API Interactivity repose sur des directives DOM (data-wp-on, data-wp-bind, etc.) qui peuvent réagir différemment selon les navigateurs. Premier réflexe donc : tester sur Chrome, Firefox et Safari, dans leurs versions récentes mais aussi sur une version antérieure (beaucoup d’utilisateurs ne mettent pas à jour immédiatement).

Ensuite, il faut vérifier la compatibilité avec les thèmes les plus utilisés : Astra, GeneratePress, ou encore le thème par défaut de WordPress. Pourquoi ? Parce que certains thèmes injectent leur propre JS ou surchargent des styles qui peuvent entrer en conflit avec vos directives. J’ai déjà vu un thème redéfinir un attribut data-* custom qui cassait littéralement l’interactivité d’un bloc. Testez donc chaque interaction critique (clic, toggle, formulaire) sur au moins 3 thèmes différents avant de valider.

Tests de non régression avec WP-CLI et environnement de staging

Pour éviter les mauvaises surprises, rien ne vaut un environnement de staging isolé. Certains hébergeurs comme Infomaniak proposent d’ailleurs du staging en un clic, ce qui simplifie grandement le processus : vous clonez votre site de prod, vous testez la migration dessus, et vous validez avant de basculer.

En parallèle, WP-CLI permet de scripter des scénarios de test reproductibles : activation/désactivation du plugin, changement de flag de version, vérification que les hooks legacy répondent toujours correctement. C’est particulièrement utile pour automatiser des tests de régression sur plusieurs configurations (multisite, différentes versions de PHP, etc.) sans tout refaire à la main à chaque fois.

Vérifier les performances avec les outils Core Web Vitals

L’un des arguments forts pour migrer vers l’Interactivity API, c’est le gain de performance. Autant vérifier que ça se confirme concrètement. Utilisez Lighthouse (intégré à Chrome DevTools) et PageSpeed Insights pour comparer les Core Web Vitals avant et après la migration.

La métrique à surveiller de près, c’est le Time to Interactive (TTI) : il devrait s’améliorer nettement, puisque l’API charge le JS de façon plus ciblée et évite le poids de jQuery. Si vous constatez une régression sur le TTI ou le CLS (Cumulative Layout Shift), c’est souvent le signe d’un script legacy qui traîne encore quelque part, ou d’un conflit entre l’ancien et le nouveau système d’interactivité.

Recueillir les retours des utilisateurs beta avant le déploiement final

Avant le déploiement massif, je recommande fortement de passer par une phase beta. Concrètement : publiez une version pre-release sur WordPress.org (avec un tag dédié), ou proposez un fork de test téléchargeable directement depuis votre dépôt GitHub. Invitez une partie de votre communauté à tester en conditions réelles, sur des configurations variées que vous n’auriez pas forcément anticipées.

Les retours beta sont précieux : ils révèlent souvent des cas d’usage exotiques (extensions tierces, configurations serveur particulières) qui passent inaperçus en interne. Et surtout, gardez toujours un plan de rollback prêt à l’emploi. Si les tests révèlent un problème bloquant, mieux vaut pouvoir revenir rapidement à l’ancienne architecture plutôt que de laisser vos utilisateurs avec un plugin cassé.