Pagination for custom WP_Query

In the example below we will display WooCommerce products (post_type=product) on a separate post page (post_type=post). I.e. we will use arbitrary query and make pagination for it.

// Requesting products
$query = new WP_Query( [
	'post_type'      => 'product',
	'posts_per_page' => 5,
	'paged'          => get_query_var( 'page' ),
] );

// Process the products received in the query, if any
if ( $query->have_posts() ) {

	while ( $query->have_posts() ) {
		$query->the_post();

		the_title();
	}

	wp_reset_postdata();
}

// Output pagination if there are more products than the requested amount
echo paginate_links( [
	'base'    => user_trailingslashit( wp_normalize_path( get_permalink() .'/%#%/' ) ),
	'current' => max( 1, get_query_var( 'page' ) ),
	'total'   => (int) $query->max_num_pages,
] );

We specified to output 5 products, if there are 22, for example, then there will be 5 pagination elements, 4 of which will be links of the following kind:

Current page, no link displayed (5 products)
http://example.com/название-записи/2/ (5 products)
http://example.com/название-записи/3/ (5 products)
http://example.com/название-записи/4/ (5 products)
http://example.com/название-записи/5/ (2 products)

Note:

  • In the parameter base where the pagination link view is formed, do not use words like page or paged. Because with page there will be a redirect from the pagination page to the post itself, and with paged there will be a 404 error on the pagination page.

  • Use get_query_var( 'paged' ) instead of the usual get_query_var( 'page' ) to get the pagination page number.

  • This code will not work on pages with content divided into several pages by the <!--nextpage--> tag, more details see here.

This Note embeded into: paginate_links()