How To Add Pagination To Your WordPress Theme

Quick Summary
- Add numbered pagination to a custom WP_Query with three pieces: the query, markup below the loop and a paginate_links helper in functions.php.
- The bfaf_theme_pagination helper reads $bf_af_query as a global, falls back to $wp_query, and formats links to match your permalink settings.
- The query takes the page number from get_query_var('paged'), and it belongs in the page template, not a template part, so the helper can read it as a global.
WordPress’s built-in pagination functions work with the main query, so when you list posts with your own WP_Query you need to add pagination yourself. These three pieces add numbered pagination to a page that lists a custom post type (before-after here): the query, the markup where the links appear, and a helper function in functions.php that prints them with paginate_links().
1. The query on the page
<?php
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
'post_type' => 'before-after',
'posts_per_page' => 3,
'paged' => $paged
);
$bf_af_query = new WP_Query($args);
?>
<?php if ($bf_af_query->have_posts()): ?>
<?php while ($bf_af_query->have_posts()):
$bf_af_query->the_post(); ?>
<a id="bf-af-card" href="<?php echo the_permalink(); ?>">
<h4><?php the_title(); ?></h4>
</a>
<?php endwhile;
wp_reset_postdata(); ?>
<?php else: ?>
<p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
<?php endif; ?>- Change
post_typeandposts_per_pageto suit your content. - The first line reads the current page number from
get_query_var( 'paged' ), falling back to 1, and'paged' => $pagedtells the query which page to load. Paginating a custom post type query uses the same line. - Keep this code in the main page template, not in a template part. The pagination function reads
$bf_af_queryas a global variable, which only works when the query is created in the global scope.
2. The pagination markup
Place this block below the loop in the same template. The wrapper uses Bootstrap grid classes (container, row, col-12); swap them for your theme’s own classes if you don’t use Bootstrap.
<div class="pagination">
<div class="container">
<div class="row">
<div class="col-12 col-lg-12 col-xl-12">
<!-- Pagination Support -->
<?php bfaf_theme_pagination(); ?>
<!-- Pagination Support -->
</div>
</div>
</div>
</div>3. The function in functions.php
// Pagination Start
// Numbered Pagination
if ( !function_exists( 'bfaf_theme_pagination' ) ) {
function bfaf_theme_pagination( ) {
$prev_arrow = is_rtl() ? '→' : '←';
$next_arrow = is_rtl() ? '←' : '→';
global $wp_query, $bf_af_query;
$total = $wp_query->max_num_pages;
$big = 999999999;
//before after query
if ( $bf_af_query ) {
$total = $bf_af_query->max_num_pages;
} else {
$total = $wp_query->max_num_pages;
}
if( $total > 1 ) {
if( !$current_page = get_query_var('paged') )
$current_page = 1;
if( get_option('permalink_structure') ) {
$format = 'page/%#%/';
} else {
$format = '&paged=%#%';
}
echo paginate_links(array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => $format,
'current' => max( 1, get_query_var('paged') ),
'total' => $total,
'mid_size' => 3,
'type' => 'list',
'prev_text' => $prev_arrow,
'next_text' => $next_arrow,
) );
}
}
}- The
function_exists()check lets a child theme overridebfaf_theme_pagination(). - It uses the page count of
$bf_af_querywhen that query exists and falls back to the main$wp_queryotherwise, so the same function also works on regular archives. - Nothing is printed when there is only one page.
- The link format follows your permalink settings:
page/2/with pretty permalinks,&paged=2without. 'type' => 'list'outputs the links as a<ul>list,mid_sizesets how many page numbers appear on each side of the current one, andis_rtl()flips the arrows on right-to-left sites.
For more background, see WPExplorer’s guide to pagination in WordPress themes.
Related articles
- numbered pagination for the main blog loop: when you don’t need a custom query
- fixing page/2/ 404 errors on archive pages: when a page link leads to a 404

