Skip to content

Query Loop: expose per-page control in Default mode to preserve archive context#78161

Open
jobthomas wants to merge 4 commits into
WordPress:trunkfrom
jobthomas:fix/73913-query-loop-archive-taxonomy-context
Open

Query Loop: expose per-page control in Default mode to preserve archive context#78161
jobthomas wants to merge 4 commits into
WordPress:trunkfrom
jobthomas:fix/73913-query-loop-archive-taxonomy-context

Conversation

@jobthomas
Copy link
Copy Markdown
Contributor

@jobthomas jobthomas commented May 11, 2026

What does this PR do?

When a Query Loop block on a category/tag/taxonomy archive page is switched from Default (inherit: true) to a Custom query (inherit: false) — often just to control the number of posts shown — the archive's taxonomy context is silently dropped and all posts appear instead of only those in the current archive.

Root cause: The most common reason users switch to Custom is to set a custom post count. That forces them out of Default mode, losing the archive scoping the main query provides for free.

Better fix (based on @youknowriad feedback): Rather than injecting archive context into custom queries, keep the block in Default mode and expose the "Items per page" control there too. The main query already carries the full archive context — so preserving it is free.

How does this fix it?

Editor (JS): The "Items per page" control in the Display panel is now visible when inherit: true (Default mode). Offset and Max pages remain Custom-only since they don't apply to the main query.

PHP: When inherit: true and a perPage value is explicitly set, the render callback re-runs the query using the main query's query_vars as its base, overriding only posts_per_page. Because query_vars already encodes the archive constraints (taxonomy, author, date, search), the scoping is preserved automatically.

When perPage is not set, the existing behaviour is unchanged — the main $wp_query is used directly as before.

Testing

Manual

  1. Create a category with some posts; leave at least one post outside that category.
  2. Open the category archive template in the Site Editor.
  3. Add a Query Loop block in Default mode.
  4. Open the sidebar → Display panel → set Items per page to 1.
  5. Expected: only 1 post appears, and it belongs to the current category — the archive context is intact.

Automated

Two new PHPUnit tests in phpunit/blocks/render-post-template-test.php:

  • test_rendering_post_template_with_per_page_override_on_default_query — verifies the per-page limit is respected.
  • test_rendering_post_template_per_page_override_preserves_archive_context — verifies the archive context (taxonomy) is preserved when a per-page override is active.

Fixes

Fixes #73913

cc @dsas @rachael-writer-editor @youknowriad

@github-actions github-actions Bot added the [Package] Block library /packages/block-library label May 11, 2026
@jobthomas jobthomas marked this pull request as ready for review May 11, 2026 12:58
@github-actions
Copy link
Copy Markdown

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: [Package] Block library.

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 11, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @rachael-writer-editor.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: rachael-writer-editor.

Co-authored-by: jobthomas <jobthomas@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: dsas <dsas@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@youknowriad
Copy link
Copy Markdown
Contributor

The behavior you're fix is intentional though. That's what the check is about. The query block can be used multiple times in the same template, say for instance to show latest posts or things like that which shouldn't inherit the context of the archive and that checkbox is what allows us to switch between the two modes.

@jobthomas
Copy link
Copy Markdown
Contributor Author

@youknowriad but the bug I found is that as soon as you add a custom config it ignores the taxonomy of the archive page. So if I just want to alter the number of posts displays, it will ignore the archive default and just display all posts. My fix will only override the taxonomy if in the custom settings the filter is used.

@jobthomas
Copy link
Copy Markdown
Contributor Author

jobthomas commented May 12, 2026

Re: approach change based on feedback

After discussing with @youknowriad, the original approach (always injecting archive taxonomy context when inherit=false and no explicit filter is set) was intentional behaviour — inherit=false is used for sidebar/footer query loops that deliberately should not inherit the archive context.

This commit reworks the fix as an opt-in instead. A new scopeToArchiveContext attribute (default false) preserves all existing behaviour. When enabled, it scopes the custom query to the current archive's taxonomy — with explicit block-level filters still taking full precedence.

What changed:

  • New "Archive" toggle in the block's Filters panel (appears when Query type is set to Custom)
  • PHP injection only fires when the toggle is enabled and no explicit taxonomy filter exists
  • Three PHPUnit tests covering: default-off (all posts shown), opted-in (archive scoped), explicit filter wins

I tested the different scenarios and this way it should be backwards compatible, Riad.

CleanShot 2026-05-12 at 16 44 50@2x

The practical use case: you can now change post count on a category archive template without the query losing its archive context — just enable the Archive filter.

@jobthomas jobthomas closed this May 13, 2026
@jobthomas jobthomas force-pushed the fix/73913-query-loop-archive-taxonomy-context branch from f3d895e to 7936ff9 Compare May 13, 2026 09:34
@gigitux gigitux had a problem deploying to WordPress packages May 13, 2026 09:57 — with GitHub Actions Failure
@gigitux gigitux had a problem deploying to WordPress packages May 13, 2026 10:20 — with GitHub Actions Error
@gigitux gigitux deployed to WordPress packages May 13, 2026 10:29 — with GitHub Actions Active
Exposes the Items per page control in the editor when the Query Loop block
is set to Default mode (inherit=true). On the PHP side, when a perPage
value is set, the main query's vars are used as the base for a fresh
WP_Query so that archive context (taxonomy, author, date, search) is
preserved while the requested limit is applied.

Fixes WordPress#73913.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jobthomas jobthomas changed the title Query Loop: respect archive taxonomy context when inherit is false Query Loop: expose per-page control in Default mode to preserve archive context May 13, 2026
The useEffect in QueryContent was resetting perPage back to the WP
reading settings value whenever inherit=true and the user changed it.
Remove that branch so the auto-sync only runs once on initialisation
(when perPage is null), leaving user-set values untouched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jobthomas
Copy link
Copy Markdown
Contributor Author

@gigitux thanks for reviewing. I actually went back to the drawing board based on feedback from @youknowriad.

The challenges with the first approach were that:

  • Technically the switch (Default/Custom) is one that goes from inherent: true to inherent: false behind the scenes. What I was trying to do is customise the inherent: true part. I still think there's value in that somehow, but it's easy to do so without a breaking change.
  • @youknowriad's proposed solution was instead to add the "number of items" selector on the Default tab as well, which is what this PR now does, and it works as expected.
  • It still means that you can't customise an archive template beyond that (e.g. if you want to split up the posts per author), but as Riad rightly pointed out: that's opening a can of worms for things like pagination.
  • I would suggest to still mark When selecting "custom" in a Query Loop block, the taxonomy of the archive is no longer respected #73913 as solved based on this PR and maybe add some workarounds for those use cases (the easiest one is to set a template up on the specific taxonomy level, so you'd have a template for each of the values). Number of items displayed is expected to fix most of the use cases of the challenge I highlighted.

With this PR, Default now has the option to change the number of items:

CleanShot 2026-05-13 at 14 12 43@2x

@jobthomas jobthomas reopened this May 13, 2026
@youknowriad
Copy link
Copy Markdown
Contributor

This is looking good to me but I tagged a couple people as the previous behavior seem to have been intentional so I'd love a second validation.

* query's vars (which already encode the current archive/search context) so that
* the archive scoping is preserved while still respecting the block-level limit.
*/
$per_page = isset( $block->context['query']['perPage'] ) ? (int) $block->context['query']['perPage'] : 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a comment about this here.

IMO updating a single value in this block that comes from the global wp_query is very risky because there are so many parts connected to the query - especially pagination.

I haven't tested, but I'm pretty confident pagination and probably the pagination query vars would be broken.

Another very important issue is that historically Query Loop block ignored the perPage setting of the block when it was using the default query. The caveat though is that themes that included this block in their templates would set this number, just for client-side preview. Later on with this PR (which is the code you're removing below) there was a small client side mitigation for proper previews.

What this means in practice is that this change will probably update (visually regress) numerous sites in the front-end, by using that default perPage number that was previously ignored.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see how perPage change can break pagination and stuff like that though? We're just saying prefer the one defined on the block (if existent) over the global one.

On sites changing, it's true, they might show more or less posts, it's not a breakage in a sense but I feel like it's the theme that should update in this case no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see how perPage change can break pagination and stuff like that though? We're just saying prefer the one defined on the block (if existent) over the global one.

Not sure about all the places but it would probably break at least the query pagination blocks that don't have similar update like the main post template block here.

it's not a breakage in a sense but I feel like it's the theme that should update in this case no?

I think that would be really difficult in practice. Maybe a workaround for that (assuming the pagination is safe in all possible places) could be a new per page prop just for the default case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about all the places but it would probably break at least the query pagination blocks that don't have similar update like the main post template block here.

That seems like a good point, all the blocks should interpret the query config in the same way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ntsekouras — the pagination concern is valid and I've been thinking through the two paths forward.

Option A (current approach + fix pagination): Keep "Items per page" in Default mode but also patch the query pagination blocks to read max_num_pages from the post template's sub-query rather than $wp_query. Mechanically correct, but it pulls in a much larger surface area and doesn't address the visual regression risk on existing sites where perPage was previously ignored.

Option B (opt-in on Custom mode): Revert the current approach and go back to an earlier commit (7bc4670) as the base — that commit injects the archive term into the Custom query's tax_query when no explicit taxonomy filter is set. Applied unconditionally it was too broad (valid pushback from @youknowriad), but as an opt-in toggle on the Custom mode it avoids the pagination problem entirely, since Custom queries already manage their own isolated WP_Query and pagination vars.

I think Option B is the cleaner path. Happy to rework if there's consensus here — wanted to flag the tradeoff before moving.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one thing we'd want to make sure is that the opt-in for "inherit" should be an option that excludes setting up filters that contradict the "inherit" (such as a category or tag filter).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey all!

I've been reading up the response to my question from a few months back and am thrilled you are giving it attention.

I don't have much to contribute technically but if this is resolved for me it would be amazing (and my SEO) being able to pin posts without two feeds as currently am.

Rachael

@rachael-writer-editor
Copy link
Copy Markdown

Thanks everyone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Block library /packages/block-library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When selecting "custom" in a Query Loop block, the taxonomy of the archive is no longer respected

5 participants