Using AJAX In Your WordPress Theme Admin

/ Wordpress / by Paul Robinson / 139 Comments
This post is very old now. While it is still valid in terms of showing how to use AJAX in WordPress, please note it lacks any form of validation or sanitization.

I recently received an e-mail asking if I could help make an admin for a theme that was powered by AJAX & therefore did not need the page to be refreshed to save the data. Unfortunately I couldn’t help as although I have built admins for plugins & themes before, I didn’t have the knowledge to add the AJAX functionality to it. However I’ve spent the past day or so reading & trying to understand the theory behind AJAX usage within WordPress’ admin & I think I’ve managed to come up with something.

Good For You! How Does That Help Me?

Well I wanted to understand it so I could write a simple tutorial for those who are getting into theme development & want to add that little bit of ‘je ne sais quoi’ to the admin of a theme.

So How Do We Start?

We add our admin page link to WordPress’ menu. Since we are going to add code into the header we need to do a few things different to a normal admin page or the header code will be output to all of the pages in the WordPress admin. Exactly what we don’t want since it is useless for anything other than running our admin page.

Here is the code to add your page into the WordPress admin:

First we call add_action() this tells WP to run a function into the admin_menu hook. The function test_add_theme_page, change the name if you like (just make sure they match), checks that the admin page we are on is actually the current file. If it is, we can add the code into the header (code coming in a minute) using the admin_head hook. No matter what page we are on we still need to add the link for the admin page into the admin menu so we run the function add_theme_page(). I explained the useage in my other tutorial, but just in case here it is again.

__(‘Test Admin’)
This is the text shown as the browser title, that’s the text shown in the title bar of your browser.
__(‘Test Admin’)
This is the text shown as the link title in the WordPress menu. It will appear under the Appearance tab
This is the role required to access the page.
This is a unique identifier. I’ve used the current file’s filename.
The function that will create the contents of the options page.

Adding the jQuery

As I mentioned earlier add_action('admin_head', 'test_theme_page_head'); adds code into the header of the page. This is what we will use to add our jQuery code. Here is the function I have used:

Ok, I’m going to attempt to explain everything written here. It might take a while so bare with me. 😆

First we define the function we named in our add_action() call. We then break out of PHP so we can easily add our jQuery code. We open a script tag as normal, then we start our jQuery with a standard run on DOM load. I’ve passed the $ through the run on DOM function so you should be able to use the standard $ inside it, however I chose to continue using the jQuery that WordPress defines for conflict prevention with other JS libraries.

Next we target our form, which hasn’t been made yet, so pick an ID or something here for it, and attach a function to it’s submit event. We serialize the data from the form, this excludes the submit buttons value & encodes data in standard URL query format, storing it in the variable data. We can use jQuery(this) because we are inside the element that is holding the data, in this case the form. Next we create a standard AJAX call using jQuery’s function. ajaxurl is a javascript global variable defined by WordPress & should always be used for AJAX requests inside WordPress for security reasons. data is our serialized form data & the the function is to be ran on the completion (callback) of our AJAX request. response is the data returned from the AJAX request.

Inside the callback we check the response data to see if it contains 1. This will be sent back by our AJAX function we will create later, if it is 1 we take it as meaning true & show an options saved box, if it is anything else we show an options not saved box. The message boxes are simple jQuery that show a message in styling defined by WordPress & then use a simple setTimeout & clearTimeout to show & hide the boxes. I’m not going to add to the length of this tutorial by explaining them, if you want to know more about them leave a comment & I’ll explain more about the functions. We finally use return false in the submit event to stop the form from submitting and refreshing the page.

Creating The HTML Form

Next we are going to create the form needed for the data to be inserted by the user. My form isn’t pretty & does absolutely nothing, by that I mean although it saves data the data isn’t used in any way. Here is my code, remember that this function is used by the add_theme_page function we called earlier:

The is the function that creates the page you see when you visit the admin page. Although you can use the header section to add your own CSS code I have just used the default WordPress CSS. That is why the HTML starts with a div with the class of wrap.

The form is the important part. You must give it a name, and the ID must be the same as the one you used in the jQuery submit event function earlier. The interior of the form can include anything your theme requires, however you need 2 extra input fields. These fields are hidden ones that send required data for the AJAX to work correctly. First is the action hidden field, the value should be whatever you want WordPress to call it’s hook that will be used to hook the function we will make to get results from the AJAX call. The other hidden field security is the infamous nonce code used by WordPress for security. The value must be as shown however you can change the word inside wp_create_nonce() to anything you like (preferable something relevent to your theme), remember it though as we’ll need it later.

We have also added in some PHP to recall the options saved in the database. We simply store the options into a variable & then echo out the data into the value fields. In the case of checkboxes we check for the value on & the add ‘checked’ if it is detected. I’ve used a ternary as it is shorter when writing PHP inline with HTML.

Creating The PHP AJAX Function

Ok so we are nearly done. We just have to create the function called by jQuery, so here’s what I ended up with:

First is add_action the name of this hook was determined by the value of our action field in our form, you just prepend wp_ajax_ to the front of it. The function can be called anything you like again, but try to keep them all relevent to your theme.

Now onto the actual function. First we use a function defined by WordPress to check if the nonce key we received is active & valid. This is where you need to recall that string I told you to remember as it has to be written in the first parameter identically to the one you wrote in wp_create_nonce(). If like me you haven’t used the default post value $_POST['_ajax_nonce'] you will need to tell the function what the $_POST key for the nonce code is, in our case security is the code. This is gotten from the name parameter of our hidden form field. Next we transfer our data from the $_POST global array to a variable called $data, we unset both the nonce key & our action which are passed by the form & are no longer needed. One thing to note is that if check_ajax_referer() does not validate the code it will die outputting ‘-1’, so when debuging if you receive that value you know what is doing it.

To stop our next piece of code throwing an error we need to get the options stored in the database & if they are empty create a blank array on the variable that would normally hold them. get_option() asks WordPress to go to the database and get the data in the wp_options table with the key provided. In our case that is test_theme. All we do is check if the returned value is an array. If it is we grab the data. If not we set a blank array.

Next it gets a little strange. I don’t know if it is a bug or not, but if the values sent from the form are the same as the current database values update_option() will return as if it failed, so we need to screen the options first. We check to see if the options in the database are the same as the once recieved from the form submission by checking for differences using array_diff(). To get the differences correctly we check both arrays against each other & then merge the results together. We also set a blank array if there is no data sent to trigger our jQuery error. If $diff is not an empty array we have differences and can safely save the options. If the update is successful stop PHP processing & echo out 1 to our jQuery function. If it fails then stop processing & echo 0 to our jQuery function. Else if the variable $diff is empty we do not need to update the database since the options are identical so just stop processing & echo out 1.

A quick note about update_option(). You can change the part in the parentheses to anything you like, but try to make it unique & relevent to your theme in some way. Also you may wonder how it is storing an array in a single database field, this is because update_option() auto serializes an array before saving it. In the same vein, get_option() unserializes a serialized array back into a normal array when it grabs data. These are important things to remember when using both these functions.

We’ll that is it, I think. I don’t think I’ve missed anything. I’ve been at this post so long I’ve lost feeling in my legs. 😆 I hope this post helps you, despite it ending up a lot more complicated that I’d wanted it to. You may need to read the post a few times to get a proper understanding of what is going on. If you have any problems or notice something I’ve missed please, please, please email me or leave me a comment & I’ll sort it out as quickly as I can.

Thank you for reading, good luck, and I hope I haven’t made your brain explode. 😉


Author’s gravatar

Hi. I have been searching far and wide for such a tutorial so thank you for this.

I’m trying to incorporate your solution into an already existing admin panel. My results are similar to Tom’s just above i.e: i get an options saved confirmation but when i refresh, my options are all gone.

Its worth noting that I already had these options saved and these are not displaying on the form anymore. I have checked the database using phpmyadmin and the data is still there but i can’t get it out or save any new data to it.

I tried your earlier suggestion of adding an alert for “response” and i was getting a 1 as expected so I’m struggling to find where the problem is.

Here’s my code as it currently stands:

I was using register_setting(‘theme_options’, ‘theme_options’, ‘validate_setting’); to save my settings before, validate_setting being the callback function. do i still need this?

Any help would be most appreciated. Ive been working on this theme for a while and this is the final piece to the puzzle.


Author’s gravatar

Just been checking and it seems as if the options are being saved in the db. however i cant retrieve them to display in the form on refresh or to be used in the theme itself i.e:

any thoughts?

Author’s gravatar

Ok, this is driving me bananas. On the front end, if i do a print_r($options) i can clearly see that the options are being changed and they are present. However, they are not being read/used correctly.

Author’s gravatar author

Hi Craig,

It’s a very strange problem you’ve got. Oddly I’ve just been working with AJAX saving on an admin for a theme I’m creating with someone & my code has changed a little since this tutorial.

I’m not sure why you aren’t able to get the info back out of the database. I’m guessing that the function settings_fields() outputs the form fields etc? Since I can’t see the function in the pastebin a few guesses. Have you globalized any variables you need such as $options to use inside the function? Are you using get_settings() instead of the newer get_option()?

Normally the trouble is getting it to save the options via AJAX, having trouble pulling them back is a new one for me. I would look at the settings_fields() function though as the problem is most likely something in there.

Author’s gravatar author

Hi Kegan,

To coin a phrase from a famous advert. It does exactly what it says on the tin. It grabs the data held in the option named test_theme from the WordPress options table.

Author’s gravatar

but where is the data in

coming from?… i don’t see it anywhere else, which is why I’m confused

Author’s gravatar author

It is where all the form data, sent from the form, is saved. You can see that it saves it on line 25 of the last block of code. The key is update_option('test_theme', $data). That tells WordPress to save all of the data inside the $data variable into the WordPress options table. If $data is an array it is auto serialized for us.

Author’s gravatar

I pretty much pasted the Javascript and AJAX PHP, but it keeps refreshing! And it doesn’t even save the data. What could I possibly be doing wrong?

Author’s gravatar

Yes, I fixed it! The form was being added dynamically, so I had to change the .submit for a .on event. Also, I had to do a little change so checkboxes work 😛
Awesome info, thanks a lot!

Author’s gravatar

For check-box just add value=”on” in the input field. Everything else works fine. Thanks for the code.

Author’s gravatar author

Ahh, I’m an idiot, I hadn’t realized that it wasn’t in the code. It looks like I’ve accidentally removed it when I was copying the code into the post.

Will add it back in so others aren’t confused. Thanks for the nudge.

Author’s gravatar


Nice tut!
How can I multiply inputs with name=”test_text” from the form using ajax? I mean for default I have a form with only one text input, but with ajax to can add more. Do you know how?


Author’s gravatar author


Adding more text inputs should be possible. It’s not so much AJAX as DOM manipulation. I can’t show you how to do it in your specific situation, but in general you’d use:

That should add your field to your form.

Author’s gravatar

nice tutorial

can you give me your email for asking.


Author’s gravatar

Hey thanks… is it possible to add this functionality to a wordpress plugin in the frontend?

Author’s gravatar

yes it works the same, you just need to specify in the js the variable ajaxurl, since wordpress doesn’t load it for front end.

To know the path, open an admin page, and in the inspect console write : ajaxurl
this will give you the path that you can copy.

and then at start of your script you can add :
var ajaxurl = ‘your_copied_url’;

Author’s gravatar

Hello, thanks for your tip, i’m stuck at the if(update_option(”, $data)) because my options aren’t in a single options name, i have multiple options like :

get_option(‘theme1’); get_option(‘theme2); get_option(‘theme3’)

Please tell me how am i supposed to write em like this 🙂

Thank you very much!

Author’s gravatar author

Hi Soheil,

I’d honestly strongly advise adjusting other parts of your code to allow saving in a single option and store your data in it as an array unless you have a very specific reason not too. It prevents clutter in the WP Options table and is (I believe) more efficient.

If you need to do it that way for any reason there aren’t many ways (I can imagine) you can use to not make the process very laborious. You can save each item manually, or use the form field name when saving the option. I’d advise against the second one unless you have a hardcoded array with the names in. Do not trust the keys in your POST array as they can be modified by malicious users before the form is sent.

Note: Also please note this post is very old now and really when making an options page you should use a form building system of some kind. You create a multi-dimensional array containing the fields you wish to output and run through that array with a function outputting the fields as it goes. This also allows you to add validation and sanitization which this tutorial is sorely missing.

Newer Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I'll keep your WordPress site up-to-date and working to its best.

Find out more