Pagination adding extra posts only on page 2

After getting so close to nailing my theme I’ve hit the last hurdle. Essentially I have three different areas on my homepage:

  1. The latest post appears in the header and styled as a featured post
  2. Two posts below that styled as recent posts
  3. The rest of the posts (10) in date order below those

Latest post (1) and the two recent posts (2) are shown using a custom WP_Query and the remaining ten posts (3) are shown using the standard loop with an offset function which also solves the pagination BUT… there’s a hiccup with page 2. For some reason, it’s adding an extra three posts to the top of the list of posts (3)? After this, page 3 onwards, it’s fine and works as it should. I should also mention that the latest and recent posts (1 and 2) are to be shown at the top at all times as you cycle through the pages.

Here the layout with the different areas and post numbers:

Layout

Here’s the code used for the function that handles the main posts (3) and the pagination fix (the offset is used to skip the latest and recent posts shown above these):

function offset_main_query ( $query ) {
     if ( $query->is_home() && $query->is_main_query() && !$query->is_paged() ) {
         $query->set( 'offset', '3' );
    }
 }
add_action( 'pre_get_posts', 'offset_main_query' );

Any help would be great as this is the last bit that’s tripping me up.

Thanks 🙂

2 Answers
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