Sometimes users ask us how to use a different number of posts per page on the first page of a listing. A use case would be to show for example 10 posts when the page loads at first, and then, each time when clicking the “Load more” Pager facet button, load 20 extra posts, until the query runs out of posts.

The issues with setting a different posts_per_page

You may be tempted to try something like the following snippet. This example uses the facetwp_query_args hook to first detect the current page number and then dynamically adapt the value for posts_per_page query argument:

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

// DON'T use this, it does NOT work. add_filter( 'facetwp_query_args', function( $query_args, $class ) { if ( $class->ajax_params['paged'] !== 1 ) { $query_args['posts_per_page'] = 8; } return $query_args; }, 10, 2);

However, this does not work as expected. WordPress calculates which posts to show on a page, based on the current page number, the number of posts per page, the total number of pages, and the total number of posts. In the example above, if the default posts_per_page is set to 10, it will show 10 posts on the first page. The second page will indeed show 8 posts as intended, but the first one will be post number 9 and not 11, so 2 posts will overlap with the first page. This happens because the query on this page assumes the first page also had 8 posts.

If the original posts_per_page is lower than the one set in the snippet for the subsequent posts, the situation is even worse. For example, if the original posts_per_page was 8 and the subsequent ones are set to 10, the second page will start at post number 11 instead of number 9. So in this scenario, two posts will be entirely missing.

How to fix a different posts_per_page for the first page

So, how do we fix the above issues? Is this even possible? As it turns out, it is, but a lot of correcting calculations need to happen:

  1. The pagination offset for subsequent pages needs to be corrected in order to show the right posts on the right (paged) page.
  2. The “Page numbers” and “Load more” Pager facet types need to be corrected to show the right number of pages.
  3. The “Result counts” Pager facet type needs to be corrected to show the correct current and total counts.

The following code uses the facetwp_query_args hook to do all of the above. The code needs to be added to your (child) theme’s functions.php.

If you are using a Listing Builder listing, make sure to replace my_listing_name in line 4 with the name of your listing. If you are using any other type of listing template, instead of using the listing template name as condition, you can use the page URI. The page URI is the part of the URL without the domain name and the query variables, and without beginning or ending slashes. See the commented out example on line 5

Next, in line 8, set the desired number of posts per page to use after the first page. The first page will use the value that is set in the query’s posts_per_page argument (or setting).

Next, if you have a “Result counts” Pager facet on your page, in line 39 replace my_pager_result_counts_facet with the name of this Pager facet. This part of the code is optional: if you are not using a “Result counts” Pager facet, you can remove line 35-63 entirely.

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

add_filter( 'facetwp_query_args', function( $query_args, $class ) { // Use a condition to let this run on a specific template or page only. if ( 'my_listing_name' == $class->ajax_params['template'] ) { // Replace 'my_listing_name' with the name of your Listing Builder listing // Alternatively, use e.g. if ( 'events/festivals' == $class->http_params['uri'] ) { to check for a page URI. // Set the posts per page for pages after the first page. $per_page_new = 6; $per_page = ( isset( $query_args['posts_per_page'] ) ) ? $query_args['posts_per_page'] : get_option( 'posts_per_page' ); $diff = $per_page_new - $per_page; $page = ( isset( $class->ajax_params['paged'] ) ) ? $class->ajax_params['paged'] : 1; // Fix pagination offset for subsequent pages if ( $class->ajax_params['paged'] > 1 ) { $offset = ( $page * $per_page_new ) - $per_page_new - $diff; $query_args['posts_per_page'] = $per_page_new; $query_args['offset'] = $offset; } // Fix "Page numbers" and "Load more" Pager facet types add_filter( 'facetwp_pager_args', function( $pager_args ) use ( $per_page, $per_page_new, $diff ) { $pager_args['total_pages'] = 1 + ceil( ( $pager_args['total_rows'] - $per_page ) / $per_page_new ); $pager_args['diff'] = $diff; if ( $pager_args['page'] > 1 ) { $pager_args['per_page'] = $per_page_new; } return $pager_args; }, 10, 1 ); // Optional: Fix "Result counts" Pager facet type add_filter( 'facetwp_facet_render_args', function( $args ) use ( $page, $per_page_new, $diff ) { // Replace 'my_pager_result_counts_facet' with the name of your "Result counts" Pager facet if ( 'my_pager_result_counts_facet' == $args['facet']['name'] ) { if ( 'counts' == $args['facet']['pager_type'] && $page > 1 ) { $total_rows = FWP()->facet->pager_args['total_rows']; $lower = ( 1 + ( ( $page - 1 ) * $per_page_new ) - $diff ); $upper = ( $page * $per_page_new - $diff ); $upper = ( $total_rows < $upper ) ? $total_rows : $upper; // If a Load more Pager is in use, force $lower = 1 if ( FWP()->helper->facet_setting_exists( 'pager_type', 'load_more' ) ) { $lower = 1; } $output = $args['facet']['count_text_plural']; $output = str_replace( '[lower]', $lower, $output ); $output = str_replace( '[upper]', $upper, $output ); $output = str_replace( '[total]', $total_rows, $output ); $args['facet']['count_text_plural'] = $output; } } return $args; }); } return $query_args; }, 10, 2);

The above code:

If you are using the above code on a WooCommerce page/template, there is possibly one more thing to fix:

Fix WooCommerce result counts

If you are using the above code on a WooCommerce products/shop page template, you may have the WooCommerce-native result counts on the page, which displays as e.g. “Showing 1–10 of 16 results”.

This result counter is generated by WooCommerce itself, but can be overwritten and fixed, by replacing the template file that it uses, located in plugins/woocommerce/templates/loop/result-count.php. This can be done as follows:

  1. In your theme directory, create a folder woocommerce (if you don’t already have one).
  2. In this folder, create a folder loop (if you don’t already have one).
  3. In this folder, create a file called result-count.php.
  4. Paste the following code into the result-count.php file:

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

<?php // Place this code in yourtheme/woocommerce/loop/result-count.php if ( ! defined( 'ABSPATH' ) ) { exit; } $page = FWP()->facet->pager_args['page']; $per_page = FWP()->facet->pager_args['per_page']; $total_rows = FWP()->facet->pager_args['total_rows']; $diff = FWP()->facet->pager_args['diff'] ?? 0; if ( $page > 1 ) { $lower= ( $page * $per_page ) - $per_page + 1 - $diff; $upper = min( $total_rows, ( $page * $per_page ) - $diff ); } else { $lower= ( $per_page * $page ) - $per_page + 1; $upper = min( $total_rows, $per_page * $page ); } ?> <p class="woocommerce-result-count"> <?php if ( 1 === intval( $total_rows ) ) { _e( 'Showing the single result', 'woocommerce' ); } elseif ( $total_rows <= $per_page || -1 === $per_page ) { printf( _n( 'Showing all %d result', 'Showing all %d results', $total_rows, 'woocommerce' ), $total_rows ); } else { printf( _nx( 'Showing %1$d&ndash;%2$d of %3$d result', 'Showing %1$d&ndash;%2$d of %3$d results', $total_rows, 'with first and last result', 'woocommerce' ), $lower, $upper, $total_rows ); } ?> </p>

If needed, while you are already overwriting the default template, you could also customize the texts it uses, in lines 27-31.

See also