I’ve been using mustache for client-side HTML generation when working with AJAX but I recently discovered that WordPress ships a slightly modified version of Underscore.js template functionality. It is located under the wp object and aptly named wp.template.

In this post, I’ll walk through how you can use the wp.template function to generate the HTML for your client-side code.

The code for the wp.template is pretty simple. This is the whole code of it, which is located in the wp-includes/js/wp-util.js file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
	 * wp.template( id )
	 *
	 * Fetch a JavaScript template for an id, and return a templating function for it.
	 *
	 * @param {string} id A string that corresponds to a DOM element with an id prefixed with "tmpl-".
	 *                    For example, "attachment" maps to "tmpl-attachment".
	 * @return {function} A function that lazily-compiles the template requested.
	 */
	wp.template = _.memoize(function ( id ) {
		var compiled,
			/*
			 * Underscore's default ERB-style templates are incompatible with PHP
			 * when asp_tags is enabled, so WordPress uses Mustache-inspired templating syntax.
			 *
			 * @see trac ticket #22344.
			 */
			options = {
				evaluate:    /<#([\s\S]+?)#>/g,
				interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
				escape:      /\{\{([^\}]+?)\}\}(?!\})/g,
				variable:    'data'
			};

		return function ( data ) {
			compiled = compiled || _.template( $( '#tmpl-' + id ).html(),  options );
			return compiled( data );
		};
	});

As we can see, WordPress uses different settings for _.template to prevent incompatibility with those PHP configurations that have asp_tags enabled. If you’re used to doing templating in Mustache, you’ll feel just right at home.

However, there are two caveats that we’ll need to be aware of when using the wp.template.

  • We need to wrap our template with an id starting with tmpl-
  • All variables that we’re referencing in the template must be prefixed with data.

Let’s go through an example of implementing this. Assuming we have an array of objects that represents WordPress users like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const users = [
    {
        "id": 1,
        "name": "John Doe",
        "email": "[email protected]",
        "isAdmin": true,
    },
    {
        "id": 2,
        "name": "Jane Doe",
        "email": "[email protected]",
        "isAdmin": false,
    },
    {
        "id": 3,
        "name": "John Smith",
        "email": "[email protected]",
        "isAdmin": true,
    },
];

We can then create a counterpart script for the template. The rules are simple:

  • Escaped data can be printed using double curly braces, eg: {{data.name}}
  • Unescaped data is using triple curly braces, eg: {{data.html}}
  • Logic can be put inside <# // our logic here #>

Using this set of rules, we can construct our template as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script type="text/html" id="tmpl-users">
    <# _(data).each(function(user) { #>
        <p>
            {{user.name}} - {{user.email}}

            <# if (user.isAdmin) { #>
                <small>Admin of this site</small>
            <# } #>
        </p>
    <# }) #>
</script>

Since the <# ... #> will be evaluated, we can practically put any Javascript inside that.

To generate the HTML, first, call the wp.template with the id of our template.

1
const userTemplate = wp.template('users');

Note: If you are getting an error of Uncaught TypeError: wp.template is not a function, you might need to add wp-util as a dependency for your enqueued script.

We don’t have to specify the full id of the template (tmpl-users) since the wp.template will automatically prepend the tmpl- part for us. The wp.template will return a callable function, so to generate the HTML, we can then do the following:

1
const userHtml = userTemplate(users);

The generated HTML should look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<p>
    John Doe - [email protected]
    <small>Admin of this site</small>
</p>
<p>
    Jane Doe - [email protected]
</p>
<p>
    John Smith - [email protected]
    <small>Admin of this site</small>
</p>

We can then insert the generated userHtml to the page using any of our preferred methods. That’s it!