Deux blocs Query Loop imbriqués affichent la même liste d’articles, ou pire, le contenu de la boucle extérieure fuite dans la boucle intérieure : ce symptôme revient régulièrement chez les intégrateurs qui composent des mises en page avec plusieurs boucles de requête sur une même page. Ce billet ne traite pas des popovers de réglage du bloc Query, déjà documentés ailleurs, mais uniquement de ce chevauchement de contexte.
Le scénario type : un bloc Query externe affiche une liste de catégories de produits, et à l’intérieur de chaque élément, un second bloc Query doit afficher les trois derniers articles liés à cette catégorie. Au lieu de cela, les deux boucles semblent partager le même jeu de résultats, ou l’une écrase le contexte de l’autre.
Symptôme : une boucle qui recopie l’autre
Concrètement, l’éditeur affiche un Query Loop parent qui parcourt correctement ses éléments, mais le Query Loop enfant, censé exécuter sa propre requête filtrée, ré-affiche à chaque itération le même article, ou reprend la pagination du parent au lieu de la sienne. Sur le front, le comportement peut différer de celui de l’éditeur, ce qui ajoute à la confusion : un aperçu qui semblait correct casse une fois la page publiée.
Ce n’est pas un bug isolé du cœur de WordPress, mais une conséquence directe du fonctionnement du contexte de bloc : chaque Query Loop transmet aux InnerBlocks qu’il contient un contexte nommé postId, postType et queryId, que les blocs enfants consomment pour savoir sur quel article ils opèrent.
Diagnostic : un queryId non déclaré ou dupliqué
La cause la plus fréquente tient en un mot : queryId. Chaque bloc Query Loop doit recevoir un identifiant unique pour que WordPress sache distinguer sa pagination et son contexte de requête de ceux d’un autre bloc Query présent sur la même page. Quand deux Query Loop imbriqués portent, par accident ou par copier-coller de blocs, le même queryId, WordPress les traite comme une seule et même boucle logique du point de vue de la pagination, même si leurs requêtes diffèrent par ailleurs.

Pour vérifier cette hypothèse, l’inspection du code source enregistré du contenu (bouton « Code editor » de l’éditeur, ou export via WP-CLI) permet de lire directement l’attribut queryId de chaque commentaire de bloc Query :
<!-- wp:query {"queryId":0,"query":{"perPage":6,"postType":"produit"}} -->
...
<!-- wp:query {"queryId":0,"query":{"perPage":3,"postType":"post"}} -->
...
<!-- /wp:query -->
<!-- /wp:query -->
Ici, les deux boucles partagent "queryId":0, ce qui est la source directe du conflit observé.
Un second piège : le contexte hérité du parent
Même avec des identifiants distincts, un bloc à l’intérieur d’un Query Loop enfant peut, s’il est mal configuré, continuer à lire le contexte postId du Query Loop parent plutôt que celui de sa propre boucle. Cela se produit typiquement quand un bloc personnalisé déclare son besoin de contexte via usesContext sans filtrer explicitement la source, et se contente du premier contexte disponible dans l’arbre.
Correctif : réattribuer des identifiants et vérifier l’héritage
La correction se fait en deux temps. D’abord, réattribuer manuellement un queryId distinct à chaque bloc Query de la page, en réenregistrant chaque boucle depuis l’éditeur plutôt qu’en dupliquant un bloc existant. Ensuite, dans un bloc personnalisé consommant le contexte, vérifier que la lecture de context.postId se fait bien à l’intérieur du bon niveau d’InnerBlocks, sans remonter accidentellement au parent.
- Supprimer puis réinsérer le Query Loop enfant plutôt que de le dupliquer, pour forcer l’attribution d’un nouvel identifiant.
- Contrôler dans le code source enregistré que les deux valeurs de
queryIddiffèrent bien. - Republier la page et comparer l’aperçu de l’éditeur au rendu réellement servi sur le front, pas seulement à l’aperçu interne.
Prévention : un identifiant explicite dès la conception
Sur un projet qui prévoit dès le départ plusieurs boucles imbriquées, il est plus sûr de documenter dans le code de l’extension ou du thème quel queryId est réservé à quelle zone de la page, plutôt que de laisser l’éditeur les attribuer automatiquement au fil des insertions et suppressions de blocs.
En résumé
Un chevauchement entre deux Query Loop imbriqués se résume presque toujours à un identifiant de requête partagé par erreur, ou à un contexte mal filtré dans un bloc personnalisé. Le réflexe à adopter reste le même : inspecter le code source enregistré avant de suspecter un bug du cœur, et vérifier systématiquement que chaque boucle porte bien son propre queryId.