WordPress Pagination: Add Page Links [Guide]

WordPress pagination guide with numbered page links

A long archive can make useful posts hard to find. Load everything at once and pages grow heavy; show only a few posts without navigation and older content feels buried. WordPress pagination splits a list into smaller pages and gives visitors clear ways to move between them.

This guide covers built-in pagination, a custom WP_Query loop, front-page edge cases, and search-friendly navigation. Use the approach that matches your template instead of adding a plugin or custom query where WordPress already handles the page numbers.

What WordPress Pagination Does

Pagination divides a collection of posts, products, or other records into separate pages. A visitor might see ten posts on page one, then follow numbered links or a Next button to reach later results. Each page gets its own URL, such as /blog/page/2/ or a query-string variant.

WordPress supports pagination in its main query by default. Themes decide how those links look and where they appear. A custom loop created with WP_Query needs its current page and total page count passed through separately. That difference explains many “page two shows page one” bugs.

Choose the Right Pagination Method

First identify which list you are changing. For a blog, category, tag, search, or custom post type archive, use the main query. For a separate list inside a page, such as featured resources beside the main content, create a custom query and paginate that query. For one unusually long article, use page breaks instead.

ContentRecommended approach
Blog or archiveMain query and theme pagination
Extra post list on a pageWP_Query plus paginate_links()
One long articlePage Break block or nextpage tag

Set Posts Per Page

For the standard blog index, go to Settings → Reading and change “Blog pages show at most.” This controls the number of posts in the main blog feed. Archive templates can have different settings, and a block theme’s Query Loop block may set its own page size, so check the template or block when the Reading value has no effect.

[screenshot] Brief description: WordPress Settings → Reading screen with “Blog pages show at most” setting highlighted.

Most classic themes already include pagination in an archive template. If yours does not, add the_posts_pagination() after the main loop in the relevant template, such as archive.php or home.php. It reads the main query’s current page and total, so you do not need to calculate them yourself.

<?php
the_posts_pagination(
    array(
        'mid_size'  => 1,
        'prev_text' => __( 'Previous', 'my-theme' ),
        'next_text' => __( 'Next', 'my-theme' ),
    )
);
?>

To change how many posts an archive displays, adjust the main query with pre_get_posts rather than replacing it with a second query. Put this in a child theme or site-specific plugin, and target only the intended query:

<?php
function site_archive_posts_per_page( $query ) {
    if ( is_admin() || ! $query->is_main_query() || ! $query->is_archive() ) {
        return;
    }
    $query->set( 'posts_per_page', 12 );
}
add_action( 'pre_get_posts', 'site_archive_posts_per_page' );
?>

When to Use a Plugin

A plugin can help when your theme offers no placement or styling controls and you do not want to edit templates. Options such as WP-PageNavi or WP-Paginate add configurable labels and page-number ranges. Check whether your theme has built-in support first: some plugins require replacing the theme’s pagination call, and enabling both systems can show duplicate links.

For custom archive logic, a plugin may not solve the underlying query issue. Keep the setup small, test it with your active theme and caching tools, and avoid installing a plugin solely to change a few colors that CSS can handle.

Paginate a Custom WP_Query

A custom post list needs two pieces of pagination data: the current page number and the query’s total pages. Set paged in the query arguments, then pass max_num_pages and the same current page to paginate_links(). The example below belongs in a page or archive template where you want a separate list.

Pass Current Page and Render Results

<?php
$current_page = max( 1, (int) get_query_var( 'paged' ) );
$posts_query = new WP_Query(
    array(
        'post_type'      => 'post',
        'posts_per_page' => 10,
        'paged'          => $current_page,
    )
);
if ( $posts_query->have_posts() ) {
    while ( $posts_query->have_posts() ) {
        $posts_query->the_post();
        printf(
            '<h2><a href="%1$s">%2$s</a></h2>',
            esc_url( get_permalink() ),
            esc_html( get_the_title() )
        );
    }
    if ( $posts_query->max_num_pages > 1 ) {
        echo paginate_links(
            array(
                'total'     => $posts_query->max_num_pages,
                'current'   => $current_page,
                'mid_size'  => 1,
                'prev_text' => __( 'Previous', 'my-theme' ),
                'next_text' => __( 'Next', 'my-theme' ),
            )
        );
    }
}
wp_reset_postdata();
?>

posts_per_page controls page size; paged selects the requested page. Use the query’s max_num_pages, not a hard-coded total, because filtering, post status, and content changes affect the number of results. The WP_Query arguments guide explains other useful query settings.

If the list is filtered by a search form or category control, preserve those filter values in each pagination URL. Otherwise, clicking page two can drop the filter and show a different set of posts. This is especially important for custom search pages; see the guide to building a custom WordPress search form.

Tip: Test page two with a URL you can reload directly. A link that works only after clicking through from page one often signals that current-page state or a filter parameter is missing.

Handle Pagination Edge Cases

Static Front Pages

When a custom query runs on a static front page, WordPress can use the page query variable for the page number rather than paged. A query that works on an archive may therefore repeat its first results on the homepage. Read the variable used by that page context and make sure the generated links use a matching URL format. Test page two on the public front end, not only in the editor preview.

Multipage Posts and Multisite

To split one long post into pages, use the Page Break block in the editor or add the <!--nextpage--> marker in the post content. The theme must call wp_link_pages() to display those links. This is different from paginating an archive of separate posts.

On WordPress Multisite, core WP_Query queries the current site; it does not combine posts across every site in the network. A network-wide list needs a network-aware query or custom aggregation, and its pagination total must represent the combined results. If switching blogs while collecting posts, restore the original blog after each switch. Do not copy a Network_Query example unless that class or its supporting plugin exists on your installation.

Make Pagination Useful for Users and SEO

Pagination is primarily a navigation and content-management tool. It can help crawlers discover archive items through ordinary links, but it does not automatically improve rankings or make a slow query fast. Make each page useful, keep URLs crawlable, and avoid treating page numbers as a substitute for a clear information structure.

  • Label navigation clearly. Use a semantic navigation element with an accessible label; keep Previous and Next text available to screen readers, even if CSS adds arrow icons.
  • Show context. Mark the current page and provide enough nearby numbers for visitors to understand where they are in a long archive.
  • Keep page size practical. A smaller page may reduce initial rendering work, but every item still has a query and template cost. Measure the page and use caching or query improvements when needed.
  • Check canonical and indexation output. SEO plugins and themes can emit canonical tags and archive metadata. Inspect actual page source after changing URL behavior; do not add competing canonical rules by hand without a specific reason.

For styling, target the classes your theme outputs and retain visible focus styles for keyboard users. Avoid hiding page numbers on mobile without replacing them with usable navigation. A compact row with Previous, current page, and Next is often easier to use than tiny numbered links.

FAQs About WordPress Pagination

How do I add numeric pagination in WordPress?

For the main archive query, call the_posts_pagination() in the archive template after the loop. For a separate WP_Query, set its paged argument and use paginate_links() with that query’s current page and maximum page count.

The current page may not be passed to the query, the wrong query variable may be read, or a static front page may need page instead of paged. Also verify that the link URL changes and that no cache or redirect rewrites every page to the first one.

Does pagination help SEO?

It can make archive content easier for visitors and crawlers to reach when pages link to one another. Pagination alone is not a ranking boost. Keep useful internal links, unique page titles where appropriate, valid canonical tags, and fast responses.

Can I paginate a custom post type?

Yes. Enable an archive when registering the post type to use its main archive query, or set post_type in a custom WP_Query. The pagination logic is the same; make sure the query and archive URL are configured for that post type.

Conclusion

Start with WordPress’s main query for archives, use a custom query only for a separate list, and pass the current page plus the true page total when building custom links. Then test direct page URLs, filters, mobile navigation, and canonical output.

Need help improving a custom WordPress theme or pagination system? Explore my WordPress customization services and reach out to discuss your project.

About The Author

Picture of Andriy Haydash

Andriy Haydash

Andriy Haydash is a WordPress Expert who helps people build and launch successful WordPress membership and e-learning websites.

Note: Not all of the articles are written directly by me.
Affiliate Disclaimer: Some links in the post may be my affiliate links

The Ultimate Managed Hosting Platform

Before YOU Leave...
Join My Newsletter

Get practical tips & tricks on how to start, grow and market your course/membership site.