Child playing Jenga

How to Offset WordPress Queries without Breaking Pagination

While designing this blog, I discovered that using the offset argument in a WordPress query can and will break pagination. I wanted to display the latest blog post in a hero section on my blog’s home page, and then all of the remaining posts in a grid below. Sounds easy, right? Indeed it is, until I realized that my pagination was no longer working.

The Problem

Apparently, this is a really common issue, and there is some fantastic documentation in the WordPress Codex that deals with this exact problem. The following is a direct quote that explains the problem in a nutshell.

… the offset argument is actually the value WordPress uses to handle pagination itself. If a developer sets a manual offset value, pagination will not function because that value will override WordPress’s automatic adjustment of the offset for a given page.

I’m not going to go over everything in the Codex, but I highly suggest you give it a read. Basically, it outlines how to use the pre_get_posts hook, in combination with the found_posts filter, to offset a specific page and/or query, and then adjust the found_posts calculation needed to fix our pagination problem. Unfortunately, the suggested code wasn’t working for me, and I needed to make a minor adjustment to the conditional statements in order for it to work.

The Codex suggests the following conditional statement in the pre_get_posts hook.

// Before anything else, make sure this is the right query...
if ( ! $query->is_home() ) {
    return;
}

This conditional merely checks whether or not the query object is on the home page. In my case, additional calls to the WP_Query object were causing this code to fail. The pre_get_posts hook was being applied to every query on the page and the results were less than desirable.

The Solution

It’s highly likely that you have more than one query on your blog’s home page. If that’s the case, we need to be a bit more explicit with our conditional statements. We need to make sure that the query is not only on our blog’s home page, but that we’re only targeting the main query on that page.

<?php   
/**
 *
 * Offset the main query on our blog's home page
 *
 */
function myprefix_query_offset(&$query) {

    // Before anything else, make sure this query is on our
    // blog's home page, and that it's the main query...
    if ( $query->is_home() && $query->is_main_query() ) {

        // First, define your desired offset...
        $offset = 1;

        // Next, determine how many posts per page you want (we'll use WordPress's settings)
        $ppp = get_option('posts_per_page');

        // Next, detect and handle pagination...
        if ( $query->is_paged ) {

            // Manually determine page query offset (offset + current page (minus one) x posts per page)
            $page_offset = $offset + ( ($query->query_vars['paged']-1) * $ppp );

            // Apply adjust page offset
            $query->set('offset', $page_offset );

        }
        else {

            // This is the first page. Just use the offset...
            $query->set('offset',$offset);

        }

    } else {

        return;

    }
}
add_action('pre_get_posts', 'myprefix_query_offset', 1 );

/**
 *
 * Adjust the found_posts according to our offset.
 * Used for our pagination calculation.
 *
 */
function myprefix_adjust_offset_pagination($found_posts, $query) {

    // Define our offset again...
    $offset = 1;

    // Ensure we're modifying the right query object...
    if ( $query->is_home() && $query->is_main_query() ) {
        // Reduce WordPress's found_posts count by the offset... 
        return $found_posts - $offset;
    }
    return $found_posts;
}
add_filter('found_posts', 'myprefix_adjust_offset_pagination', 1, 2 );

Leave a Reply

Your email address will not be published. Required fields are marked *

Are you in need of a freelance web developer?

Hire me