Comment afficher une deuxième page de résultats d’une Query Loop sans recharger la page entière ? C’est la question posée par un client éditeur qui voulait un annuaire d’articles paginé, avec une transition fluide digne d’une application, mais sans ajouter React ni aucune bibliothèque front supplémentaire au thème.
La réponse tient dans l’Interactivity API, devenue stable avec WordPress 6.5. Elle fournit un jeu de directives HTML (data-wp-interactive, data-wp-on, data-wp-context) et une petite bibliothèque cliente qui remplace avantageusement du jQuery artisanal, tout en restant légère et pensée pour les blocs.
Étape 1 : préparer le bloc de pagination
La pagination native de la Query Loop repose sur le bloc core/query-pagination, qui génère des liens classiques (<a href>) vers des URL avec un paramètre de page. Pour la rendre interactive sans casser le fonctionnement de base (accessibilité, indexation), on ne remplace pas le bloc : on l’enrichit via un bloc enfant personnalisé enregistré avec son propre block.json, doté de "supports": { "interactivity": true }.
{
"name": "monclient/pagination-douce",
"supports": { "interactivity": true },
"viewScriptModule": "file:./view.js"
}
Étape 2 : déclarer le store côté serveur et côté client
Le rendu PHP du bloc initialise l’état partagé avec wp_interactivity_state(), en y plaçant la page courante et l’identifiant de la Query Loop ciblée :

wp_interactivity_state( 'monclient/pagination-douce', array(
'pageCourante' => 1,
'chargement' => false,
) );
Le fragment de template associe ensuite le contexte au conteneur de la Query Loop via wp_interactivity_data_wp_context(), ce qui permet à chaque instance du bloc sur la page de garder son propre état, indépendamment des autres.
Étape 3 : écrire les actions côté client
Le fichier view.js importe store depuis @wordpress/interactivity et déclare une action déclenchée au clic sur le lien « page suivante » :
import { store, getContext } from '@wordpress/interactivity';
store( 'monclient/pagination-douce', {
actions: {
*pageSuivante() {
const context = getContext();
context.chargement = true;
const reponse = yield fetch(
`/wp-json/wp/v2/posts?page=${ context.pageCourante + 1 }`
);
const articles = yield reponse.json();
context.pageCourante += 1;
context.chargement = false;
// Mise à jour du DOM via la directive data-wp-each côté template.
},
},
} );
La directive data-wp-on--click="actions.pageSuivante" posée sur le lien de pagination remplace le comportement de navigation classique par cet appel asynchrone, sans empêcher le lien de fonctionner en JavaScript désactivé grâce à l’attribut href conservé en repli.
Étape 4 : afficher un état de chargement pendant la requête
Le contexte partagé chargement permet d’afficher un indicateur visuel discret pendant l’appel REST, via une simple directive data-wp-class--is-chargement="context.chargement" posée sur le conteneur de la liste d’articles. Aucune classe JavaScript à gérer manuellement : l’Interactivity API se charge de synchroniser la classe CSS avec l’état.
Ce que l’on a mesuré
Sur les projets où cette approche a été mise en place, le gain le plus net se ressent sur mobile : la suppression du rechargement complet de page évite le clignotement du fond d’écran et le décalage de mise en page (repaint du header, des polices web). Sur desktop, la différence perçue est plus discrète, la navigation étant déjà rapide grâce au cache serveur.
- Le temps entre le clic et l’affichage des nouveaux articles reste sous la seconde sur une connexion correcte.
- Le poids ajouté au bundle JavaScript est minime, l’Interactivity API étant déjà chargée par le cœur dès qu’un bloc l’utilise.
- Le comportement de repli (navigation classique) fonctionne sans configuration supplémentaire.
Sur ce projet, on a délibérément laissé le lien de pagination classique fonctionnel en arrière-plan : la directive interactive vient en surcouche, jamais en remplacement du HTML natif.
Pour aller plus loin
Cette implémentation ne couvre volontairement pas la mise à jour de l’URL (gestion de l’historique de navigateur) ni les aspects d’accessibilité complets d’une pagination dynamique (annonce ARIA live des nouveaux résultats, gestion du focus). Ces points méritent un traitement à part entière, que ce soit via l’API History native ou des régions aria-live dédiées. La documentation officielle sur l’Interactivity API reste la référence à suivre pour approfondir les directives disponibles.