Load More Posts with AJAX for WordPress Multisite

Here is a screenshot how I did it for WordPress default TwentySeventeen theme. You can also download my child theme with the ready code.

AJAX load more button for posts within a WordPress Multisite network

Below I will guide you step by step how to implement it on your own WordPress Multisite Network.

Step 1. Creating a Load More Button

If you’re going to use it not for WordPress Multisite but for your custom queries, just replace Network_Query with WP_Query – that’s all.

$args = array(
	'posts_per_page' => 5 // you can add any query parameters here
$query = new Network_Query( $args );
if( $query->have_posts() ) :
	while( $query->have_posts() ...
		// the posts will be displayed here
	... endwhile;
	// if there is more than 1 page of posts – display the button
	if ( $query->max_num_pages > 1 ) :
		echo '<div class="misha_loadmore">More posts</div>';

Step 2. How to Pass Parameters to the AJAX request correctly?

When I publish my first tutorial about creating a load more button for WordPress archive pages and search results, I passed all the parameters using wp_localize_script() function. And it is OK when you create load more buttons for the main loop.

But we can not do the same for custom queries.

So I’m going to break it down into two parts.


We still have to use wp_localize_script() for some of the parameters. Well, only for ajaxurl 😁Anyway passing it for every loop doesn’t make any sense for me.

add_action( 'wp_enqueue_scripts', function(){
	// I hope you do not forget that we create all of this with the help of jQuery
	// This script will contain all the code which will process our load more button
	wp_register_script( 'multisite_loadmore', get_stylesheet_directory_uri() .'/multisite_loadmore.js', array('jquery') );
	// And finally creating object of parameters with wp_localize_script()
	wp_localize_script( 'multisite_loadmore', 'misha_loadmore_params', array(
		'ajaxurl' => admin_url( 'admin-ajax.php' )
	) );
	wp_enqueue_script( 'multisite_loadmore' );
} );

Data Attributes

Obviously, ajaxurl is not enough data to pass to our AJAX script. We have to pass some information related to our query as well. Here they are:

Let’s come back to the first step and add all of these parameters as data attributes for our button.

	... endwhile;
	// if there is more than 1 page of posts – display the button
	if ( $query->max_num_pages > 1 ) {
		echo '<div class="misha_loadmore" data-args="' . esc_attr( json_encode( $args ) ) . '" data-max-page="' . $query->max_num_pages . '" data-current-page="1">More posts</div>';

You probably noticed that I set a static value current-page parameter. What do you think why?

Because it is always going to be equal to 1. I suppose that you’re not going to use pagination functionality for custom queries. But if you are, you can get the current page from the query var get_query_var( 'paged' ). More info about implementing it is in this tutorial.

Step 3. Creating an AJAX request in multisite_loadmore.js

My purpose was to create a code which will work for you regardless of how many custom queries you are going to use on the same page.

That’s why I passed the current page value in a data attribute too.

	 * Button click event
	$('body').on('click', '#misha_loadmore', function(){
		var button = $(this),
		    queryArgs = button.data( 'args' ),
		    maxPage = button.data( 'max-page' ),
		    currentPage = button.data( 'current-page' );
			url : misha_loadmore_params.ajaxurl, // AJAX handler
			data : {
				'action': 'multisiteloadmore', // the parameter for admin-ajax.php
				'query': queryArgs,
				'page' : currentPage,
			type : 'POST',
			beforeSend : function ( xhr ) {
				button.text('Loading...'); // some type of preloader
			success : function( data ){
				button.text('More Posts').data( 'current-page', currentPage ).before(data);
				if( currentPage == maxPage ) {
		return false;

Step 4. wp_ajax_ and wp_ajax_nopriv_

The code for your functions.php:

add_action('wp_ajax_multisiteloadmore', 'misha_multisite_loadmore'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_multisiteloadmore', 'misha_multisite_loadmore'); // wp_ajax_nopriv_{action}
function misha_multisite_loadmore(){
	$args = $_POST['query'];
	$args['paged'] = $_POST['page'] + 1; // next page of posts
	$query = new Network_Query( $args ); // or you can use query_posts() for custom queries
	if( $query->have_posts() ) :
		// run the loop
		while( $query->have_posts() ): $query->the_post();
			// post template

If you remember my previous tutorial, to got the $args inside wp_ajax_ hooks we used json_decode() function. Why we don’t use it here?

Because $_POST['query'] is already a ready-to-use array. Thankfully to data() method:

queryArgs = button.data( 'args' ),

But if instead of data() method you decide to use attr(), you still have to json_decode() the value before creating $args array for Network_Query.


Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X