Skip to content

Query Loop: Fix sticky posts repeated on every page - #83604

Open
Vrishabhsk wants to merge 1 commit into
WordPress:trunkfrom
Vrishabhsk:fix/sticky-post-loop
Open

Vrishabhsk wants to merge 1 commit into
WordPress:trunkfrom
Vrishabhsk:fix/sticky-post-loop

Conversation

@Vrishabhsk

Copy link
Copy Markdown
Contributor

What?

Closes #80624

Limit sticky posts to the first page of a Query Loop, so they are no longer repeated on every paginated page.

Why?

A Query Loop that includes sticky posts repeats the sticky posts at the top of every paginated page. On page 1 the sticky posts are moved to the top as expected; on pages 2 and beyond the same sticky posts are prepended again, so they appear more than once and the page renders more items than "Items per page".

The cause is that build_query_vars_from_query_block() paginates custom queries with the offset query var alone and never sets paged. WP_Query decides whether to relocate sticky posts from paged, not offset:

if ( $this->is_home && $page <= 1 && is_array( $sticky_posts ) && ! empty( $sticky_posts ) && ! $query_vars['ignore_sticky_posts'] ) {

When paged is unset, $page is 0, so $page <= 1 is true on every request and the sticky posts are prepended each time.

Related: #69867, #71088, https://core.trac.wordpress.org/ticket/63365.

How?

Adds a query_loop_block_query_vars filter in the WordPress 7.2 compat layer (lib/compat/wordpress-7.2/query-block.php) that sets paged to the current page for paginated queries, matching the main query and the REST API.

The filter only runs when the block has a numeric perPage, which is the same condition under which build_query_vars_from_query_block() paginates. offset is still set by core and still drives the LIMIT, so the posts returned for each page are unchanged; the only difference is that WP_Query now relocates sticky posts on the first page only.

This is intentionally the narrow fix: page 1 keeps its existing behavior (sticky posts at the top), and no query attribute, block attribute, REST argument, or default changes, so there is no backwards-compatibility impact.

Testing Instructions

  1. Create 12 or more published posts.
  2. Mark an older post (not one of the most recent) as sticky.
  3. Add a Query Loop block and set "Items per page" to a value smaller than the number of posts, for example 4.
  4. Preview the front end.
  5. Confirm page 1 shows the sticky post at the top.
  6. Go to page 2 and confirm the sticky post is no longer repeated.

Run the unit tests:

npm run test:unit:php -- --filter Gutenberg_Query_Block_Paged_Test

Use of AI Tools

  • AI tooling was used to investigate the root cause, draft the implementation, and write the tests.
  • The change was reviewed and verified manually.
  • Model used : Deepseek v4.1 Flash by Ollama

Core backport

The change lives in the lib/compat/ layer and needs a Core backport. It is covered by the existing Core PR WordPress/wordpress-develop#8750 (Trac https://core.trac.wordpress.org/ticket/63365), which adds the same paged query var.

`build_query_vars_from_query_block()` paginates custom queries with
`offset` alone and never sets `paged`. `WP_Query` relocates sticky posts
to the top based on `paged`, so when it is unset every page is treated as
the first one and the sticky posts are prepended again on pages 2 and up.

Set `paged` for paginated queries so sticky posts are placed on the first
page only. `offset` still drives the LIMIT, so the posts returned for each
page are unchanged.

Closes WordPress#80624.
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

🎉 Props

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @Vrishabhsk, @iritfab.

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: Vrishabhsk, iritfab.

Co-authored-by: rahultank-spaceo <rahultank@git.wordpress.org>
Co-authored-by: Dibyajyotikabi <dibyajyotikabi@git.wordpress.org>
Co-authored-by: wdjina <jns141191@git.wordpress.org>
Co-authored-by: Swanand01 <swanandm@git.wordpress.org>
Co-authored-by: robelsust <therssoftware@git.wordpress.org>

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

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

@iritfab

iritfab commented Sep 28, 2026

Copy link
Copy Markdown

Thanks for this. I am currently on holiday but will test it next week. Not sure if I understood everything, I am no developer.

But there is still a second issue: given again a loop with 12 posts. If the sticky post is older (let's say no. 15) there will be on the first page 13 posts!

I never worked with github, not sure how to address this correctly

@rahultank-spaceo

Copy link
Copy Markdown

Test Report

Patch tested: #83604

Environment

Steps

  1. Created 8 published posts (Post 01 – Post 08, dated 1–8 January 2026) and made the older post "Post 02" sticky.
  2. Created a page with a Query Loop block (custom query, 3 items per page, sticky posts included) with Post Title, Post Date and Pagination.
  3. Viewed page 1 and page 2 of the Query Loop on the front end.
  4. Repeated the same steps with the PR build.

Results

  • Before the patch ❌: page 1 showed Post 02 (sticky), Post 08, Post 07, Post 06. Page 2 showed Post 02 again, then Post 05, Post 04, Post 03, so the sticky post was repeated.
  • With the patch ✅: page 1 was unchanged (Post 02 at the top, then Post 08, Post 07, Post 06). Page 2 showed Post 05, Post 04, Post 03, and the sticky post was not repeated.

Expected result

The sticky post appears at the top of page 1 only and is not prepended again on page 2.

Additional notes

  • On page 3, "Post 02" still shows once, in its normal date position (Post 02, Post 01), both before and after the patch. This matches how the main blog query handles sticky posts, so it looks expected, but I'm mentioning it for completeness.
  • Page 1 shows 4 posts with "Items per page" set to 3, because the sticky post is added on top. This is also the same before and after the patch.

Screenshots

Before patch (page 2):
80624-before

With patch (page 2):
80624-after

@Dibyajyotikabi

Copy link
Copy Markdown

Tested this on WP 7.1.3 + Gutenberg 24.2.0 (Twenty Twenty-Five, headless Chrome on Linux), using the same setup as my report on #80624: Query Loop not inheriting, 4 per page, newest first, sticky posts included, one older post marked sticky, 17 posts total.

  • The sticky post no longer repeats. It's only at the top of page 1 now, and pages 2–5 show posts 12–9, 8–5, 4–1 and the last one.
  • Pages 2–4 show 4 posts each. Page 1 still shows 5, because the sticky post isn't counted against the per-page limit.
  • The sticky post also shows up again in its normal date spot on page 4.

Both of those last two match how the main blog query behaves, so they may be expected. Page count and the last page look right.

@shimotmk shimotmk added [Block] Query Loop Affects the Query Loop Block [Type] Bug An existing feature does not function as intended labels Oct 20, 2026

This branch has not been deployed

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

Labels

[Block] Query Loop Affects the Query Loop Block [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sticky Post does not work correctly with loop (2 issues!)

5 participants