“Load more” Anything in WordPress with 20 lines of code! (infinite scroll)

“Load more” Anything in WordPress with 20 lines of code! (infinite scroll)

Leo ・5 min read

You may want to add a modern look and feel for the WordPress pagination style, like a Load more button or an infinite scroll effect.

You could try getting this effect using WP AJAX by creating a new Query and populating it with arguments.

Still, if you’re like me, you’ll feel like it’s wrong, bizarre, and messy to handle a lengthy request to get to the next page of posts, with a massive list of parameters and every possible scenario that posts can also have, given the fact you that you want this to work with categories and slugs like tags and sometimes even with custom posts.

I know it’s a lot of headaches doing this without using any plugins, but again, if you’re like me, I try to stray away from plugins to gain knowledge, and if something goes wrong, I’ll be familiar with my code base.

The path I chose

My approach to solving this puzzle is that I want the code to be DRY.

So, I know that in the standard HTTP request, WordPress is already doing the heavy lifting for us with every request, so I needed to understand how WordPress handles the request, and then I’ll disguise the Ajax request as an HTTP request to get back the results sound easy? Yes, it is.

Mocking the request 

Since we need to work with the main query, The plan is to make WordPress think it’s a standard HTTP request and get back the results.

So I dag down in WordPress source code.

function wp( $query_vars = '' ) {
	global $wp, $wp_query, $wp_the_query;

	$wp->main( $query_vars );

	if ( ! isset( $wp_the_query ) ) {
		$wp_the_query = $wp_query;
  • The main is set up by wp() function in wp-includes/functions.php .
  • a call to $wp->main() function in the WP Class is what creates the main query.
public function main( $query_args = '' ) {
	$parsed = $this->parse_request( $query_args );
	if ( $parsed ) {

	* Fires once the WordPress environment has been set up.
	* @since 2.1.0
	* @param WP $wp Current WordPress environment instance (passed by reference).
	do_action_ref_array( 'wp', array( &$this ) );
wp-includes/class-wp.php view on Github
  • The parse_request function is what writes or rewrites the main query by parsing the request and finds the correct WordPress Query.
list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] );
$self = $_SERVER['PHP_SELF'];
wp-includes/class-wp.php view on Github
  • By handling specific request parameters like $_SERVER['PHP_SELF']; and $_SERVER['REQUEST_URI'] and with just these two properties we can trick WordPress into building the query.

$_SERVER properties ‘PHP_SELF’ and ‘REQUEST_URI’ 

  • The PHP_SELF is the filename of the currently executing script, and we know that the page generating the posts is always index.php.
  • The REQUEST_URI is the URI given to access this page, e.g. ( for posts is / and for tags /tag/100daysofcode ), we can get this information form the client javascript by Ajax and send the current URL path as a parameter.

Let’s create an Ajax

function load_more_handler() {

    $page = $_POST['currentPage'] + 1;
    $path = $_POST['path'];
    // * mock index.php call 
    $_SERVER['REQUEST_URI'] = $path;
    $_SERVER['PHP_SELF'] =  '/index.php';
    wp(array('paged' => $page, 'post_status'=> array('publish')));

    if (have_posts()) {
        while (have_posts()) {

    } else {

add_action('wp_ajax_load_more_all', 'load_more_handler');
add_action('wp_ajax_nopriv_load_more_all', 'load_more_handler')

The Ajax request should hold two parameter; The two parameters are currentPage and path

The currentPage parameter is just the current page number and we can inject it by PHP to the HTML as data-attribute, then get with JavaScript and send it with the request.

The path parameter it’s the path name of the current page and we can get it in JavaScript by window.location.pathname and send it with the request. (see the js code)

Set the properties

  • $_SERVER['REQUEST_URI'] = $path;
  • $_SERVER['PHP_SELF'] = '/index.php';

Finally we know we need to call the wp() function to build the main query, and we passed an array of arguments

wp(array('paged' => $page, 'post_status'=> array('publish')));

Note that we strictly asked the main query only to show us the published posts because “if the Query is run in an admin context (administration area or AJAX call), protected statuses are added too.”

You can read more about the status parameters here.

Creating the loop to get the posts using ob_start() and ob_get_clean().

you can read more about the Output Control Functions in the PHP docs here.

Also, it’s better if you’d like create a template for a single post layout and get it using get_template_part to maintain clean code.

The Javascript client code

let url = '/wp-admin/admin-ajax.php';
let action = 'load_more_all';

// this is set by php get_query_var('paged');
let currentPage =  $('#articles').data('currentpage'); 

let path = window.location.pathname;

// this function increments the html data-attribute
funciton nextPage() {
    $('#articles').data('page', parseInt(this.currentPage) + 1);

    type: 'POST', url, data: { action, currentPage, path },
    success: function (res) { 
    error: function (err) {  console.error(err); }

You can see it in action on the current site

I hope you enjoyed reading it. I would love to hear your thoughts.


Read Next

Implementing Fast Image Blurring Algorithms in C# (Photoshop level) Gaussian Blur

In Reality Gaussian Blur Algorithm uses the Gaussian function it is really slow to compute for large input...