Custom wp_query pagination – next_posts_link() or wp_pagenavi() always empty

I’m aware this is a topic discussed to death, but I have read through many solutions and can’t understand why my pagination simply isn’t working.

I have a very simple custom wp_query:

Read More
$current_page = (get_query_var('paged')) ? get_query_var('paged') : 1;

$project_query = new WP_query(array(
  'post_type' => 'project',
  'paged' => $current_page,
  'posts_per_page' => 50
));

There are about 2000 projects in there, so without a doubt, there are pages.

This is on my index.php of the theme.

No matter what, the URL /p/pageno returns a 404. The function next_posts_link('Older Entries »', $project_query->max_num_pages); is always empty.

I have tried, based on reading several similar questions:

  1. Setting reading settings to display 1 post max at a time, to ensure pagination kicks in.
  2. Installing wp_pagenavi – same result
  3. Setting the homepage to a static page with this code in a custom page template – this finally works somewhat (/p/2 etc don’t 404), but then next_posts_link() stops at page 2 and goes no further.
  4. change get_query_var('paged') to get_query_var('page') in the custom page template
  5. rename $project_query to $wp_query

I’m at a loss, this should be so simple, but pagination always seems to be the one thing that kills me when working with WordPress.

Related posts

2 comments

  1. I think most pagination issues stem from a misunderstanding of the query and template loading process that happens before you are executing code within a template file.

    When someone requests a page:

    1. WordPress parses the request and decides what it’s for based on the incoming URL. For example, is it the main root page, does it begin with category, does it match the pattern of a single post, does it have a page number, etc.. The correct query vars are set from this process.

    2. WordPress runs the main query on the database, using the query vars set in the last step.

    3. WordPress looks at the results of the main query, and determines whether it was successful or produced no results. If it was successful, WordPress loads the correct template for that type of request, or loads the 404 template if it wasn’t.

    So now you’re finally in the template, if everything above went as expected. But now we can see the source of one problem – if that main query produced no results, you’ll never reach the template to run your custom query. That main query has no relation to the custom query in your template. Whether or not your custom query has results for that page number is irrelevant, if it doesn’t have a corresponding page in the main query. The answer in this case, is to modify the main query before it happens via pre_get_posts, to make that query give us the results we want instead of running another query in the template.

    The other option, which you mention in your question, is to use a static page. This is the exception to the above behavior regarding the main query – a static page will return that single page’s content regardless of what page number you set in the URL. This is how you can get around the 404 issues with main queries. In that case, you must reference the correct query object when using the next/previous posts link functions, as Kaiser mentions in his answer.

  2. Your main problems are:

    • You got no Error reporting/debug turned on: WP_Query vs. WP_query
    • Inside your posts link call, you’re referring to the main query instead of your custom query.

    Look at this:

    $project_query = new WP_Query( array(
      'post_type'      => 'project',
      'paged'          => get_query_var('paged') ? get_query_var('paged') : 1,
      'posts_per_page' => 50
    ) );
    var_dump( get_next_posts_link( "Older Entries →", $project_query->max_num_pages ) );
    

Comments are closed.