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.

$paged = max( 1, get_query_var('page') );

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

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

		// your code here
		the_title();
	}

	wp_reset_postdata();

	// Display pagination if the number of products is greater than the requested amount
	echo paginate_links( [
		'base'    => user_trailingslashit( wp_normalize_path( get_permalink() .'/%#%/' ) ),
		'current' => $page,
		'total'   => $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.

Note embeded into: paginate_links()