Pagination is broken

Pagination

WordPress has the ability to split lists of posts or a single post into multiple pages for “paged” navigation. You can set how many posts to list on each page on the Reading screen (wp-admin > Settings > Reading). The “Blog pages show at most” value will be used by WordPress unless your theme overrides it, such as when it is using a custom query. When multiple loops (posts lists) are used in a theme template file only one loop, the main loop, can be paginated.

WordPress offers builtin function to navigate through the posts. Theme authors can use simple links for previous and next page, or numbered pagination.

Function Reference

Multiple Posts Pagination
Single Post pagination

Example Loop with Pagination

This simplified example shows where you can add pagination functions for the main loop. Add the functions just before or after the loop.

<?php if ( have_posts() ) : ?>

<!-- Add the pagination functions here. -->

<!-- Start of the main loop. -->
<?php while ( have_posts() ) : the_post();  ?>

<!-- the rest of your theme's main loop -->

<?php endwhile; ?>
<!-- End of the main loop -->

<!-- Add the pagination functions here. -->

<div class="nav-previous alignleft"><?php next_posts_link( 'Older posts' ); ?></div>
<div class="nav-next alignright"><?php previous_posts_link( 'Newer posts' ); ?></div>

<?php else : ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

See this example if you’re querying the loop with WP_Query.

Troubleshooting Broken Pagination

Sometimes pagination will break and give unexpected results, redirect you to the wrong page or give you a 404 (page not found) on the “paged” pages. This is usually due to your theme altering (querying) the main loop wrong.

Basic Troubleshooting Steps

De-activate all plugins to rule out plugins breaking the pagination. If this works, re-activate the plugins one by one until you find the problematic plugin(s). If you are using plugins for your pagination see the plugin section of this page.

Re-save your permalink structure on the Permalinks Screen (wp-admin > Settings > Permalinks) or set it to the default structure to see if that fixed it.

Ask for help in the forums if you find the “Advanced Troubleshooting Steps” below a bit too technical or if it didn’t help solve your broken pagination. Provide links if possible to a page demonstrating the problem, and where you downloaded your theme from. State what troubleshooting steps you did or did not do. Bonus points if you have read the forum welcome, searched for a solution yourself, located and submitted the template file where the pagination is broken to a pastebin and added a link to it in your forum topic.

Advanced Troubleshooting Steps

The next steps are for when your theme is querying the loop and is using one of the Multiple Posts Pagination functions.

For this advanced section it is assumed you have a basic understanding of the following:

Important! Make a backup of your theme template files and create a child theme before editing them.

Step 1. Locating the template file

The first thing we need to know, is on what theme template file the pagination is broken. Looking at the template hierarchy can help you figure out which template file is used. Or locate the file with the use of a plugin.

Step 2. Finding the Main Paginated Loop

Open the template file that you found in the previous step and find the main loop. The main loop (if there is more than one loop) is the one that has one of the Multiple Posts Pagination functions before and/or after it. Your theme may be using a plugin or some other method of pagination if you didn’t find any of the functions.

Adding the “paged” parameter to a query

If WP_Query is altering the main loop and the “paged” parameter is not set you’ll need to add it with get_query_var(). This is so WordPress knows exactly what page it’s on.

For example, if your query looks like this (without the “paged” parameter):

<?php $the_query = new WP_Query( 'posts_per_page=3' ); ?>

you add the parameter like this:

<?php
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

$the_query = new WP_Query( 'posts_per_page=3&paged=' . $paged ); 
?>

The next example is exactly the same as above but with the parameters in an array:

<?php
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$args = array(
  'posts_per_page' => 3,
  'paged'          => $paged
);

$the_query = new WP_Query( $args ); 
?>

Passing variables to query posts() or new WP_Query().

static front page

If the pagination is broken on a static front page you have to add the “paged” parameter this way:

<?php 
if ( get_query_var( 'paged' ) ) { $paged = get_query_var( 'paged' ); }
elseif ( get_query_var( 'page' ) ) { $paged = get_query_var( 'page' ); }
else { $paged = 1; }

$the_query = new WP_Query('posts_per_page=3&paged=' . $paged); 
?>

Removing query_posts from the main loop

query_posts() isn’t meant to be used by plugins or themes. Use WP_Query instead. It accepts the same parameters as query_posts does. Be aware that both of these methods are not the most efficient, way to alter the default query. Usually it’s also responsible for breaking pagination. If your theme is using it to query the main loop you can replace it with the preferred way, that is to say, hooking into ‘pre_get_posts‘ and altering the main query by using is_main_query(). This way is more reliable and faster because the query for the main loop is altered before the posts are retrieved from the database. This method works on all template files except Page templates. See this example on how to do a query on a Page template by using WP_Query.

For example, lets say your theme queries the main loop like this on your home page (index.php) and category pages (category.php) and the pagination is not working:

<?php 
// the query to set the posts per page to 3
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array('posts_per_page' => 3, 'paged' => $paged );
query_posts($args); ?>
<!-- the loop -->
<?php if ( have_posts() ) : while (have_posts()) : the_post(); ?>
		<!-- rest of the loop -->
		<!-- the title, the content etc.. -->
<?php endwhile; ?>
<!-- pagination -->
<?php next_posts_link(); ?>
<?php previous_posts_link(); ?>
<?php else : ?>
<!-- No posts found -->
<?php endif; ?>

Remove the query_posts part from the template files (index.php, category.php) used in this example:

<?php 
// query to set the posts per page to 3
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array('posts_per_page' => 3, 'paged' => $paged );
query_posts($args); ?>

And add the query for your home and category pages back in your theme’s functions.php file:

function my_post_queries( $query ) {
  // do not alter the query on wp-admin pages and only alter it if it's the main query
  if (!is_admin() && $query->is_main_query()){

    // alter the query for the home and category pages 

    if(is_home()){
      $query->set('posts_per_page', 3);
    }

    if(is_category()){
      $query->set('posts_per_page', 3);
    }

  }
}
add_action( 'pre_get_posts', 'my_post_queries' );

As you can see we can make use of conditional tags (is_home(), is_category(), etc…). This lets us target the pages where we want to alter the query. Lets say you want 6 posts on a category page called “Movies”. We can add this to the my_post_queries() function above to target that category:

    // alter the query for the Movies category page 
    if(is_category('Movies')){
      $query->set('posts_per_page', 6);
    }

Read more about querying with pre_get_posts:
Customize the WordPress query
When to use WP_Query, query_posts and pre_get_posts
Querying posts without query_posts

Numbered Pagination

To show a set of page numbers with links to the previous and next pages or without, use this code:

With previous and next pages:

the_posts_pagination( array(
	'mid_size'  => 2,
	'prev_text' => __( 'Back', 'textdomain' ),
	'next_text' => __( 'Onward', 'textdomain' ),
) );

Without previous and next pages:

the_posts_pagination( array( 'mid_size'  => 2 ) );