How to use JavaScript for WordPress REST API (AJAX)

Have you seen a new section gets loaded without a page refresh? This technique is called AJAX and we will learn how to do that

Have you seen a new section gets loaded without a page refresh?

This is using a JavaScript technique called AJAX where the client can call API to retrieve data and append it to the current page.

Let’s learn by creating a “LOAD MORE” button to show the next set of posts.

Important: This tutorial is only intended to teach you about AJAX and not a good way to create a Load More button. It’s far better if you create a custom API endpoint and re-use the existing template-parts from your theme to render.

Lesson #1: Choosing a Library

Although it’s possible to do without any library, it’s easier with one like React, Handlebars, or Template7.

React is the library used in Gutenberg editor. It is powerful yet hard to learn and definitely overkill for a Load More button.

On the other hand, Handlebars and Template7 are very simple libraries. They have nearly identical syntaxes but the latter is 4x smaller in size (50KB vs 12KB).

So we decided to use Template7 for this tutorial. Download it here or use the CDN link at:

Lesson #2: Create & Render a Template

The template is written inside <template> tag:

<template id="template-name">
  <p> Hello, my name is {{ firstName }} {{ lastName }} </p>

If you’re wondering, that’s a legit HTML tag specifically used to hold content that will be rendered with JavaScript.

You can render the template by passing in an object like this:

let rawTemplate = document.querySelector('#template-name').innerHTML;
let template = Template7.compile( rawTemplate );

let html = template({
  firstName: 'John',
  lastName: 'Doe'

// the `html` variable contains the rendered template
// you can use it to insert anywhere you want

Here’s a CodePen showcasing the snippets above:

See the Pen Template 7 Basic Showcase by hrsetyono (@hrsetyono) on CodePen.

Lesson #3: Conditional and Loop


The conditional in Template7 is limited to checking true or false like below:

<h4>{{ firstName }} {{ lastName }}</h4>
<p>{{ bio }}</p>

{{#if showEmail }}
  <p>Email: {{ email }}</p>
{{else }}
  <p>Email is hidden</p>
{{/if }}

See the Pen Template 7 – Basic Showcase 02 by hrsetyono (@hrsetyono) on CodePen.


Let’s say we have this posts data:

    title: 'Hello World',
    excerpt: 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta debitis quisquam, ullam officiis.',
    link: ''

    title: 'Second Post',
    excerpt: 'Voluptatem obcaecati corrupti eius culpa, placeat rem eligendi deserunt earum ut possimus dolores',
    link: ''

Here’s the template to loop them:

{{#each this }}
    <h2> <a href="{{ link }}">
      {{ title }}
    </a> </h2>
    <p>{{ excerpt }}</p>
{{else }}
  <li>There's no post</li>
{{/each }}

See the Pen Template 7 – Basic Showcase 03 by hrsetyono (@hrsetyono) on CodePen.

this refers to the current variable. Since we passed in an array, we don’t have a name to refer the variable, so we use this.

Lesson #4: API Helper

The native API function (called fetch) is quite wordy. So it will make your life easier to create these helper functions as a shortcut:

const myAPI = {
  get( endpoint ) {
    return window.fetch( endpoint, {
      method: 'GET',
      headers: { 'Accept': 'application/json' }
    } )
    .then( this._handleError )
    .then( this._handleContentType )
    .catch( this._throwError );

  post( endpoint, body ) {
    return window.fetch( endpoint, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify( body ),
    } )
    .then( this._handleError )
    .then( this._handleContentType )
    .catch( this._throwError );

  _handleError( err ) {
    return err.ok ? err : Promise.reject( err.statusText )

  _handleContentType( res ) {
    const contentType = res.headers.get( 'content-type' );
    if( contentType && contentType.includes( 'application/json' ) ) {
      return res.json()
    return Promise.reject( 'Oops, we haven\'t got JSON!' )

  _throwError( err ) {
    throw new Error( err );

How to use it:

// GET request
myAPI.get( url ).then( result => {
  // do something
} );

// POST request url, data ).then( result => {
  // do something
} );

Lesson #5: Calling for API

Let’s try calling the Posts API of WPTips website and display it. By combining what we have learned, this is quite simple to do:

See the Pen Template 7 – Lesson 05 – API Call by hrsetyono (@hrsetyono) on CodePen.

Lastly, we created a Load More button with click listener and added pagination parameter to the API URL like shown below:

See the Pen Template 7 – Lesson 05b – API Call with Load More by hrsetyono (@hrsetyono) on CodePen.

Lesson #6: Using it in WordPress

Enqueue your custom JS and Template7:

add_action( 'wp_enqueue_scripts', function() {
  $js_dir = get_template_directory_uri() . '/js';
  wp_enqueue_script( 'my-template', $js_dir . '/my-template.js', [], '', true );
  wp_enqueue_script( 'template7', '', [], '', true );
} );

Find a way to output the <template> tag. For example, we can echo it in footer:

add_action( 'wp_footer', function() { ?>

  <template id="template-posts">
    {{#each this }}
      <h3> <a href="{{ link }}">
        {{ title.rendered }}
        </a> </h3>
      <p>{{ excerpt.rendered }}</p>

    {{else }}
    <p>There's no post</p>
    {{/each }}

<?php } );


You have learned how to do API calls with JavaScript and rendering it using the infamous “Mustache” syntax (because the curly brace looks like a mustache).

That should bring you up to speed with the basic of AJAX.

If you are interested to learn more, the first thing you should read next is Template7 official docs.

Thank you for reading. If you have any question, feel free to post it in the comment below 🙂

Default image
Henner Setyono
A web developer who mainly build custom theme for WordPress since 2013. Young enough to never had to deal with using Table as layout.
Leave a Reply