Guide: How to Properly Use AJAX in WordPress

AJAX is a technique you can use in Javascript for fetching data from server and updating the contents of your page without reloading it in your browser. In this post we’ll dive into how to properly use AJAX in WordPress.

Most of you are probably familiar with performing AJAX requests outside WordPress, and want to look into how to go about doing it in WordPress. You might be wondering about the following questions:

  1. Which URL you should send your AJAX requests to in your Javascript.
  2. How to actually catch the AJAX request in PHP, fetch its data and send something back.
  3. Making sure your AJAX requests are secure.

In WordPress you can perform AJAX requests in admin and/or frontend; the process is the same with a few differences in answering the above questions. Let’s dive right in!

The basics of performing AJAX request in WordPress

The first thing you need is, obviously, the javascript where you want to send AJAX request. It should be enqueued by using a valid method of adding scripts to WordPress, either frontend or backend. Most likely you already have a script set up where you just need the part of sending or requesting something from WordPress.

Posting AJAX in WordPress requires you to pass a Javascript object variable as data to the request. In the object you need to provide as minimum one property; ‘action‘. The value of the ‘action‘ property is a string that can be (almost) anything you want. This action is what tells WordPress how to route the AJAX request. But it also defines part of the hook name you will need to hook onto in PHP for handling the AJAX request.

When it comes to answering question 1 above: what URL you should send your AJAX request to? This differs depending on if your script is enqueued in admin or frontend. Because AJAX is already built into WordPress administration screens you can simply use the global variable ajaxurl. But for frontend requests it gets a bit more complicated because you need to provide the AJAX URL manually along with your Javascript. More on that below.

For handling AJAX requests in PHP you hook onto the action wp_ajax_<your action name>. If you want to run AJAX requests in frontend for non-logged-in users you can also hook onto wp_ajax_nopriv_<your action name>. More on that below. Inside your hooked function you can access data passed from Javascript by referencing the $_POST array. For outputting something back you simply echo something. That’s the short answer to question 2 above.

Let’s dive right into the code. We’ll start with how to add AJAX requests in admin panel.

Performing AJAX requests in WordPress admin

I assume you already have a script enqueued which reacts to some user action, gathers data and is ready to perform an AJAX request, either for updating something, or for requesting some information back from WordPress. The code below is a very simplified example; the code enqueues a Javascript file in theme to admin, and inside the Javascript code it sets up some random variables, performs AJAX at page load, and outputs the response in console.

functions.php
add_action('admin_enqueue_scripts', function() {
	wp_enqueue_script('awhitepixel-script', get_stylesheet_directory_uri() . '/assets/js/awhitepixel-ajax.js', ['jquery'], '', true);
});
awhitepixel-ajax.js
jQuery(document).ready(function($) {
	var data = {
		'action': 'awhitepixel_send_something',
		'something': 'Hello world',
		'another_thing': 14
	}
	$.post(ajaxurl, data, function(response) {
		console.log(response);
	});
});

As you can see in the Javascript code above you set up a Javascript object (think of it like an array) with all data you want to pass on, including the important ‘action‘ property.

Also as mentioned above, since AJAX is already built into WordPress administration screens, you simply use the global variable ajaxurl as AJAX URL.

Now we’ll add code for accepting this AJAX request in PHP. Because I provided ‘awhitepixel_send_something‘ as value of ‘action‘, I need to hook onto wp_ajax_awhitepixel_send_something.

functions.php
add_action('wp_ajax_awhitepixel_send_something', function() {
	$something = $_POST['something'];
	$another_thing = (int) $_POST['another_thing'];

	echo 'You sent ' . $something . ' with ' . $another_thing;

	wp_die();
});

Inside your hooked function you have full access to WordPress; you can use any WordPress functions, queries and access global variables (e.g. $wpdb for database queries). All variables you passed in Javascript will be available inside $_POST array with the keys you provided in the Javascript object.

It’s optional to pass something back to Javascript (for example when you only want to send a request to update or save something and don’t need confirmation). If you want to pass something back, simply echo your output. For outputting more complex variables than simply a string, you should return it as a json encoded array so that Javascript can read and parse it properly. Something like this;

$pass_back = ['something' => 'a value', 'another_thing' => 42];
echo json_encode($pass_back);

One important final note on the last line; wp_die(). All AJAX callback functions should “close the thread” or terminate properly by doing die() or exit(). In WordPress we use wp_die() for proper integration but the result is the same. If you happen to get weird “0” in your AJAX responses, it’s most likely because you didn’t do wp_die() or die(). Always, always make sure “you die” at the end of all functions hooked onto wp_ajax.

Performing AJAX requests in WordPress frontend

The process of performing AJAX in frontend is the same as in admin, but somewhat more complicated in answering question 1: what URL should AJAX post to. In the above example for performing AJAX in admin we already had a global Javascript variable, ajaxurl, but this variable is not available frontend. We need to provide it ourselves. For getting the value of WordPress’ AJAX URL you use admin_url('admin-ajax.php')(yes, “admin url” for frontend).

When enqueuing a frontend script that will perform AJAX requests in your theme or plugin, you need to pass on WordPress’ AJAX URL as variable to that Javascript, by using wp_localize_script().

functions.php
add_action('wp_enqueue_scripts', function() {
	wp_enqueue_script('awhitepixel-ajaxscript', get_stylesheet_directory_uri() . '/assets/js/frontendajax.js', ['jquery']);
	$variable_to_js = [
		'ajax_url' => admin_url('admin-ajax.php')
	];
	wp_localize_script('awhitepixel-ajaxscript', 'Theme_Variables', $variable_to_js);
});

Now, in your Javascript you have access to a global Javascript object Theme_Variables (this name can be anything you want), with WordPress’ AJAX URL as value to ‘ajax_url‘ property. Your AJAX function would be something like this:

frontendajax.js
jQuery(document).ready(function($) {
	var data = {
		'action': 'awhitepixel_frontend_stuff',
		'something': 'Hello world',
		'another_thing': 14
	}
	$.post(Theme_Variables.ajax_url, data, function(response) {
		console.log(response);
	});
});

When dealing with AJAX in frontend there’s one more thing to keep in mind regarding the hook you use to accept AJAX requests. You still hook onto wp_ajax_<your action name>, but this hook will only work for visitors that are logged in. If you want to perform AJAX requests for visitors that are not logged in, you need to (also) hook onto wp_ajax_nopriv_<your action name>. Normally for frontend AJAX requests that’s required for the theme to work properly you would hook onto both, pointing them both to the same function.

functions.php
add_action('wp_ajax_awhitepixel_frontend_stuff', 'awhitepixel_ajax_frontend');
add_action('wp_ajax_nopriv_awhitepixel_frontend_stuff', 'awhitepixel_ajax_frontend');
function awhitepixel_ajax_frontend() {
	$something = $_POST['something'];
	$another_thing = (int) $_POST['another_thing'];

	echo 'You sent ' . $something . ' with ' . $another_thing;
	
	wp_die();
}

As for the content of your AJAX hooked function it’s the same as for admin requests. You have complete access to WordPress, get ahold of passed variables from $_POST, optionally return something by performing echo, and you should always, always remember to “die” with wp_die() at the very end.

Securing your AJAX requests

Because AJAX makes it so easy to pass data between script and backend, you should definitely take necessary steps to make your code secure. That includes sanitizing any data before saving it, but also utilizing WordPress’ “nonce” functionality to check if the requests are coming from corrent and accepted locations. Read WordPress’ documentation on nonce if you are unfamiliar with it.

The basic premise is this; you use a WordPress method by either generating a nonce value (wp_create_nonce()) or generating a hidden input field with the nonce as value (wp_nonce_field()). In your AJAX request you pass on the nonce value (which is encoded by WordPress) in the request, and in the accepting part (wp_ajax_ hook) you verify the nonce with what you set originally.

Let’s add some security to the example code we did above. First we’ll generate a nonce and pass it in our wp_localize_script along with AJAX URL. As parameter to wp_create_nonce() we set some unique string that only we should know about, so to speak. In the example below I’m going for ‘awhitepixel-nonce‘.

functions.php
add_action('wp_enqueue_scripts', function() {
	wp_enqueue_script('awhitepixel-ajaxscript', get_stylesheet_directory_uri() . '/assets/js/frontendajax.js', ['jquery']);
	$variable_to_js = [
		'ajax_url' => admin_url('admin-ajax.php'),
		'nonce' => wp_create_nonce('awhitepixel-nonce')
	];
	wp_localize_script('awhitepixel-ajaxscript', 'Theme_Variables', $variable_to_js);
});

What gets passed as ‘nonce‘ in our Javascript variables to the frontend is a randomized number generated by WordPress. Nobody but us, authors of this script, know that the valid nonce is ‘awhitepixel-nonce‘. In our Javascript, we pick up the nonce value and pass it right back in our AJAX request:

frontendajax.js
jQuery(document).ready(function($) {
	var data = {
		'action': 'awhitepixel_frontend_stuff',
		'something': 'Hello world',
		'another_thing': 14,
		'nonce': Theme_Variables.nonce
	}
	$.post(Theme_Variables.ajax_url, data, function(response) {
		console.log(response);
	});
});

And finally in our wp_ajax hooked function we should start by checking if the nonce was valid with wp_verify_nonce(). We provide the unique string we set above, and if WordPress doesn’t match the unique encoded value passed on with this string, we terminate immidiately:

functions.php
add_action('wp_ajax_awhitepixel_frontend_stuff', 'awhitepixel_ajax_frontend');
add_action('wp_ajax_nopriv_awhitepixel_frontend_stuff', 'awhitepixel_ajax_frontend');
function awhitepixel_ajax_frontend() {
	if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'awhitepixel-nonce')) {
		wp_die(-1);
	}

	$something = $_POST['something'];
	$another_thing = (int) $_POST['another_thing'];

	echo 'You sent ' . $something . ' with ' . $another_thing;
	
	wp_die();
}

Leave a comment