Pagination adding extra posts only on page 2

Here’s a suggestion for a general way to support different number of posts on the home page than on the other paginated pages. We should use the main query instead of sub-queries, if possible.

Formula

It seems to make sense to take the offset for paginated pages as:

offset_op = ( paged - 1 ) * pp_op + ( pp_fp - pp_op ) + offset_fp           
          = ( paged - 2 ) * pp_op + pp_fp + offset_fp 

where paged (pagination), pp_fp (posts per first page), pp_op (posts per other pages) and offset_fp (offset for the first page) are non-negative integers.

For paged=1 the offset is offset_fp, else it’s offset_op for other pages.

Example #1:

First we calculate the offset for few pages to better understand this:

For paged=1:
    offset_fp = 0

For paged=2:
    offset_op = (2-2)*10 + 13 + 0
              = 13

For paged=3:
    offset_op = (3-2)*10 + 13 + 0
              = 10+13
              = 23
...

Here’s a list of post indices on each page:

0,1,2,3,4,5,6,7,8,9,10,11,12    (offset_fp=0,  pp_fp=13, paged=1)
13,14,15,16,17,18,19,20,21,22   (offset_op=13, pp_op=10, paged=2)
23,24,25,26, 27,28,29,30,31,32  (offset_op=23, pp_op=10, paged=3)
...

We can see that the offset matches the indices.

Example #2:

Let’s take pp_fp = 3, pp_op = 5, offset_fp=4 and calculate the offset_op:

For paged=1:
    offset_fp = 4

For paged=2:
    offset_op = (2-2)*5 + 3 + 4
              = 7

For paged=3:
    offset_op = (3-2)*5 + 3 + 4
              = 5+3+4
              = 12
...

and compare it to the indices:

4,5,6          (offset_fp=4,  pp_fp=3,  paged=1)
7,8,9,10,11    (offset_op=7,  pp_op=5,  paged=2)
12,13,14,15,16 (offset_op=12, pp_op=5,  paged=3)
...

Demo Plugin

Here’s a demo implementation:

/**
 * Plugin Name: WPSE demo
 */
add_action( 'pre_get_posts', function( \WP_Query $query ) 
{
    // Nothing to do if backend or not home page or not the main query
    if ( is_admin() || ! $query->is_home() || ! $query->is_main_query() )
        return;

    // Get current pagination
    $paged = get_query_var( 'paged', 1 );

    // Modify sticky posts display
    $query->set( 'ignore_sticky_posts', true );

    // Modify post status
    $query->set( 'post_status', 'publish' );

    // Edit to your needs
    $pp_fp      = 13; // posts per first page
    $pp_op      = 10; // posts per other pages
    $offset_fp  = 0;  // offset for the first page

    // Offset for other pages than the first page
    $offset_op = ( $paged - 2 ) * $pp_op + $pp_fp + $offset_fp;

    // Modify offset
    $query->set( 'offset', $query->is_paged() ? $offset_op : $offset_fp );

    // Modify posts per page
    $query->set( 'posts_per_page', $query->is_paged() ? $pp_op : $pp_fp );  
} );

Hope you can adjust it to your needs!

Leave a Comment