Wordpress Load More Posts with AJAX. Step by Step Tutorial. No plugins.

Creating Load More button for WordPress is really simple. As an option I will show you how to load more posts on scroll.

Step 1. Load more button

We begin with the button HTML. Here is just one main rule – do not show the button if there are not enough posts. We will check it with $wp_query->max_num_pages

global $wp_query;  
if (  $wp_query->max_num_pages > 1 )
 echo '<div class="ui_loadmore">More posts</div>'; 

I inserted the button just under the standart pagination, but you can remove it as well. To style the button the according way use CSS below.

 background-color: #ddd;
 border-radius: 2px;
 display: block;
 text-align: center;
 font-size: 14px;
 font-size: 0.875rem;
 font-weight: 800;
 text-transform: uppercase;
 padding: 10px 0;
 transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.3s ease-in-out;  
 background-color: #767676;
 color: #fff;

Step 2. Enqueue jQuery and loadmore.js. Pass query parameters to the script.

Well, that’s where the magic happens. This small piece of code allows you to pass the according parameters to the script that’s why the button can work on any page – tags, categories, post type archives, search etc.


function ui_load_more_scripts() {
 global $wp_query; 
 // In most cases it is already included on the page and this line can be removed
 // register our main script but do not enqueue it yet
 wp_register_script( 'ui_loadmore', get_stylesheet_directory_uri() . '/loadmore.js', array('jquery') );
 // now the most interesting part
 // we have to pass parameters to loadmore.js script but we can get the parameters values only in PHP
 // you can define variables directly in your HTML but I decided that the most proper way is wp_localize_script()
 wp_localize_script( 'ui_loadmore', 'ui_loadmore_params', array(
  'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php', // WordPress AJAX
  'posts' => serialize( $wp_query->query_vars ), // everything about your loop is here
  'current_page' => get_query_var( 'paged' ) ? get_query_var('paged') : 1,
  'max_page' => $wp_query->max_num_pages
 ) );
  wp_enqueue_script( 'ui_loadmore' );
add_action( 'wp_enqueue_scripts', 'ui_load_more_scripts' );

If you still do not know, the above code is for your functions.php. And please note,  load more button is adapted for the main loop only. If you need it for a custom loop, ask me in comments.

Step 3. loadmore.js – what is inside?

It is a small JS file, actually you can place it anywhere you want, for simpleness I decided to place it just in a theme directory (line 9 of previous code).

Option 1. Load More button

If you choose the option 1, skip the option 2 and vice versa.

  var button = $(this),
      data = {
   'action': 'loadmore',
   'query': ui_loadmore_params.posts, // that's how we get params from wp_localize_script() function
   'page' : ui_loadmore_params.current_page
   url : ui_loadmore_params.ajaxurl, // AJAX handler
   data : data,
   type : 'POST',
   beforeSend : function ( xhr ) {
    button.text('Loading...'); // change the button text, you can also add a preloader image
   success : function( data ){
    if( data ) { 
     button.text( 'More posts' ).prev().before(data); // insert new posts
     if ( ui_loadmore_params.current_page == ui_loadmore_params.max_page ) 
      button.remove(); // if last page, remove the button
    } else {
     button.remove(); // if no data, remove the button as well

Option 2. No button, just load posts on scroll

 var canBeLoaded = true, // this param allows to initiate the AJAX call only if necessary
     bottomOffset = 2000; // the distance (in px) from the page bottom when you want to load more posts
  var data = {
   'action': 'loadmore',
   'query': ui_loadmore_params.posts,
   'page' : ui_loadmore_params.current_page
  if( $(document).scrollTop() > ( $(document).height() - bottomOffset ) && canBeLoaded == true ){
    url : ui_loadmore_params.ajaxurl,
    beforeSend: function( xhr ){
     // you can also add your own preloader here
     // you see, the AJAX call is in process, we shouldn't run it again until complete
     canBeLoaded = false; 
     if( data ) {
      $('#main').find('article:last-of-type').after( data ); // where to insert posts
      canBeLoaded = true; // the ajax is completed, now we can run it again

Step 4. wp_ajax_

This is the AJAX handler function. Insert it to your functions.php file.

function ui_loadmore_ajax_handler(){
 // prepare our arguments for the query
 $args = unserialize( stripslashes( $_POST['query'] ) );
 $args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
 $args['post_status'] = 'publish';
 // it is always better to use WP_Query but not here
 query_posts( $args );
 if( have_posts() ) :
  // run the loop
  while( have_posts() ): the_post();
   // look into your theme code how the posts are inserted, but you can use your own HTML of course
   // do you remember? - my example is adapted for Twenty Seventeen theme
   get_template_part( 'template-parts/post/content', get_post_format() );
   // for the test purposes comment the line above and uncomment the below one
   // the_title();
 die; // here we exit the script and even no wp_reset_query() required!
add_action('wp_ajax_loadmore', 'ui_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'ui_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}

If you have any questions, please ping below comment box.

Related Post

Latest Post

Recent Posts Widget

Make sure to never miss a thing...

Get the latest news from the creative industry along with other creative goodies, conveniently delivered to social media.