Kishan Jasani

Code, Creativity & Everything I Learn

How pagination works with custom query?

Pagination with Custom Queries

Pagination is one of those features users never think about, but everyone feels its absence. Long pages load more slowly, feel overwhelming, and are harder to scan. Pagination addresses this by breaking content into smaller, more readable chunks.

WordPress does a great job of automatically handling pagination in the main loop. But the moment you create a custom query using WP_Query, you step outside that safety net. Pagination will not work unless you wire it up yourself.

This guide walks you through exactly how custom pagination works, why it breaks, and how to fix it properly.

Main Loop vs Custom Query (Why Pagination Breaks)

When WordPress loads a page like /blog/page/2, it already knows which page of results to fetch. Functions like:

  • previous_posts_link()
  • next_posts_link()

Work because they rely on the main query.

The moment you write this:

$custom_query = new WP_Query( $args );

You’re telling WordPress:
“Don’t worry, I’ll handle everything.”

That includes pagination.

So now WordPress needs you to answer one simple question:

Which page of results should I show right now?

How Pagination Works with WP_Query?

Pagination for custom queries always comes down to three things:

  1. Detecting the current page number
  2. Passing that page number into the query
  3. Rendering pagination links based on the query result

Miss any one of these, and pagination breaks.

Let’s go step by step.

1. Detect the Current Page

WordPress stores the current page number in query variables. Most of the time, you’ll use paged.

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

If the user is on page 1, this returns 1.
If they’re on /page/3/, this returns 3.

Simple, but critical.

2. Pass paged into Your Query

Now you tell WordPress which slice of posts you want.

$args = array(
    'post_type'      => 'news',
    'posts_per_page' => 4,
    'paged'          => $paged,
);

That paged argument is what makes pagination actually work.

Without it, WordPress will always return page 1, no matter which page the user clicks.

3. Run the Loop and Render Pagination

Here’s the complete working example.

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

$args = array(
    'post_type'      => 'news',
    'posts_per_page' => 4,
    'paged'          => $paged,
);

$custom_query = new WP_Query( $args );

if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        the_title( '<p>', '</p>' );
    endwhile;

    // Pagination
    if ( function_exists( 'pagination' ) ) {
        pagination( $custom_query->max_num_pages );
    }

    wp_reset_postdata();
endif;

Two important details here:

  • Pagination functions must use $custom_query->max_num_pages
  • wp_reset_postdata() is mandatory after custom queries

Skipping the reset can break menus, sidebars, and any other components that rely on the main query.

The Most Common Mistake: paged vs page

This single issue causes more pagination bugs than anything else in WordPress.

paged

Used for:

  • Archives
  • Blog index
  • Category pages
  • Most listings

page

Used when:

  • Your query runs inside a static page
  • You are on a Page template

If pagination leads to a 404 or always shows page 1, this is usually why.

Pagination on a Static Page (Correct Way)

If your custom query lives inside a Page (not an archive), use this:

$paged = get_query_var( 'page' ) ? get_query_var( 'page' ) : 1;

$args = array(
    'post_type'      => 'post',
    'posts_per_page' => 4,
    'paged'          => $paged,
);

Notice something important:
You still pass the value into paged, even though you read it from page.

That detail matters.

Safe Approach (Works Everywhere)

If you want to avoid guessing which one WordPress is using, combine both:

$paged = get_query_var( 'paged' )
    ? get_query_var( 'paged' )
    : ( get_query_var( 'page' ) ? get_query_var( 'page' ) : 1 );

This works across:

  • Archives
  • Static pages
  • Custom templates

Custom pagination in WordPress isn’t hard, but it is unforgiving. One wrong variable name, and the whole thing collapses.

Once you understand how WordPress tracks page numbers and how WP_Query Consumes them, pagination becomes predictable and reliable.

Leave a Reply