Excerpt: A common layout need: left-aligned “previous” link, centered page numbers, right-aligned “next” link. Setting the Pagination block’s items justification to “Space Between” achieves this — until you reach the first or last page. The previous/next child block is not rendered when not applicable, so the pagination block ends up with two items and the center/nav shift left or right. This note explains two practical fixes and an accessible, performant filter-based solution.
The Gutenberg core pagination-block can be used inside a Query-loopblock and contains three childblocks: a block showing a link to the previous page with posts, a block with pagenumbers and a block showing a link to the next page. The links to the next and previous page can be a textlabel or an arrow or a combination of the two.
I wanted this pagination on the bottom of my page and i wanted the previous-page arrow to align left, the pagenumbers in the middle and the next-page arrow to align right. To achieve this, i selected the pagination block and in its toolbar I selected ‘Change Items Justification’. After selecting ‘Space Between Items’, I accomplished what I wanted.

However, when visiting the first page, all got mis-aligned because when there is no ‘Previous Page’ ( when you are on the first page ) the block does not render into an empty page-element, but it gets not rendered at all. That leaves the pagination-block with only two items and since alignment is set to space between this means the pagenumbers move to the left. Something similar happens on the last page of the query.
I want the elements of pagination to be fixed and not be moving around while navigating between pages.
My first thought was to use the child blocks of the pagination-block on their own. I made a block with 3 columns and tried to move the ‘Previous Page’ block to the left column, the ‘Page numbers’ block to the middle column and the ‘Next Page’ to the right column. That way, i thought, when the ‘Previous Page’ element disappears, the column that contains it is still there, keeping the layout fixed.
I reconned being a child of the Query Loop block would give these blocks enough context to work on their own. But regretably this was not possible.
In the block-meta of the query-pagination-previous block, I found that only the pagination block is an allowed parent:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "core/query-pagination-previous",
"title": "Previous Page",
"category": "theme",
"parent": [ "core/query-pagination" ],
"description": "Displays the previous posts page link.",
You just can’t drag the Previous-Page-Block to an empty column or other block to help you fix its position.
First solution
You can’t move a pagination childblock outside the pagination-block. But you can remove them from the pagination block. You can make three columns each containing a Pagination-block. Since all three instances of the pagination block get their context from the same Query-loopblock, they act in excactly the same way. Now you can remove the pagenumbers and the next-page blocks from the left pagination-block. You remove both previous- and nextpage blocks from the middle pagination block and then remove previous-pageblock and pagenumbers from the right pagination block.
I did not investigate on the cost of extra queries that need to be made using this admittedly not very elegant solution. But it works.
Second solution
The previous-page and next-page render to empty content when they are not applicable. But ofcourse after rendering, all blocks go through some filters, also when they result in empty content.
So adding a filter to the rendering of these blocks enables us to replace the empty content with some empty placeholder element that ensures justifying the items always takes place with the same numbers of items. Some inline style regarding the margins is added here, but this can ofcourse also be achieved by adding some classes / css*.
add_filter('render_block_core/query-pagination-previous', function( $block_content, $parsed_block, $block_object ) {
if( '' === $block_content) {
$block_content = '<span class="wp-block-query-pagination-previous" style="margin-inline-end: auto;"></span>';
}
return $block_content;
}, 10, 3 );
add_filter('render_block_core/query-pagination-next', function( $block_content, $parsed_block, $block_object ) {
if( '' === $block_content) {
$block_content = '<span class="wp-block-query-pagination-next" style="margin-inline-start: auto;"></span>';
}
return $block_content;
}, 10, 3 );
Since this is obviously something that attributes to the looks of the website, it should probably be added to the theme’s functions.php or somewhere that gets invoked from there.
Conclusion
Two pragmatic options: three synchronized Pagination blocks (no code) or the lightweight filter+CSS approach (recommended). The filter approach preserves layout, is accessible and performant, and keeps editor ergonomics unchanged.
* You can ask Copilot for this, but I did it for you and it provided also some accessability additions on the run:
For your functions.php:
<?php
add_filter( 'render_block_core/query-pagination-previous', function( $block_content, $parsed_block, $block ) {
if ( '' === trim( $block_content ) ) {
return '<span class="my-pagination-placeholder pagination-previous" aria-hidden="true" role="presentation"></span>';
}
return $block_content;
}, 10, 3 );
add_filter( 'render_block_core/query-pagination-next', function( $block_content, $parsed_block, $block ) {
if ( '' === trim( $block_content ) ) {
return '<span class="my-pagination-placeholder pagination-next" aria-hidden="true" role="presentation"></span>';
}
return $block_content;
}, 10, 3 );
And for the style.css
.my-pagination-placeholder {
display: inline-block;
width: 1rem; /* reserve horizontal space, tune as needed */
height: 1em;
vertical-align: middle;
}
.wp-block-query-pagination { /* ensure flex layout remains */
display: flex;
align-items: center;
}
.pagination-previous { margin-inline-end: auto; }
.pagination-next { margin-inline-start: auto; }
Leave a Reply