Settings API Explained

Update: Plugin Options Starter Kit

I have now written a Plugin options starter kit for people who want to learn how to put together a Plugin options form using the WordPress Settings API. You can see more information about the Plugin and download the latest version here.

Have fun, and be sure to let me know what other features you want added in future versions!

In this post I discuss the WordPress Settings API and how you can leverage them for use in your own plugins. Firstly there is coverage of the API in general and the various functions available together with their usage. After that there is a tutorial section with an example walk through of how to add options to a new sub menu page under the Settings Menu.

The Settings API was first added in WordPress 2.7 to make things more efficient when it came to adding options pages for your plugins (and themes) to the WordPress admin area. Until recently I was still doing things the ‘old’ way which involved doing all the form creation and submission handling yourself, checking for $_POST variables, manual security checks etc. Whilst this worked fine it was also cumbersome and time consuming to implement, and making changes was awkward.

Adding new options to an existing plugin was not the breeze it should have been and you certainly thought twice before committing to a plugin options revision! Also, all of the security implementation had to be done manually too. Now, with the Settings API all the security checks are handled automatically which is a huge bonus! You don’t even need to create the options form table yourself if you don’t want to, this is taken care of for you too. Basically, all you have to do is to register your options with the Settings API and specify the input fields you wish to add to the options page, and away you go!

The motivation behind writing this post is to bring together the information on how the Settings API can really make your life easier when developing plugins. Now, there is a clean way you can construct your options pages quickly leaving you with more time to focus on the core plugin functionality. Also making amendments later on is no trouble at all now.

Settings API Functions

There are six general functions that you will be using to get the most out of the Settings API. For quick reference, all the functions you need to get started are listed below, with full parameter specification as defined in the core WordPress files. The coloured function parameters (red, green, blue) indicate how particular parameter values overlap between the various Settings API functions. Any coloured parameters should hold identical values between functions to make everything work properly. All the other parameters not coloured are allowed to have different values between functions (e.g. all the $callback parameters should contain different values as they refer to separate function calls).

  • settings_fields($option_group);
  • register_setting($option_group, $option_name, $sanitize_callback=“”);
  • unregister_setting($option_group, $option_name, $sanitize_callback=“”);
  • add_settings_section($id, $title, $callback, $page);
  • add_settings_field($id, $title, $callback, $page, $section, $args = array());
  • do_settings_sections($page)

The parameters and what they do are listed next:

  • $option_group – unique group name for option set
  • $option_name – name of each option (more than one option in the same register_settings() function requires an array of options)
  • $sanitize_callback=“” – section/field callback function to validate option data
  • $id – unique ID for the section/field
  • $title – the title of the section/field (displayed on options page)
  • $callback – callback function to be executed
  • $page – options page name (use __FILE__ if creating new options page)
  • $section – ID of the settings section (needs to be the same as $id in add_settings_section)
  • $args = array() – additional arguments

Let’s now go through each of these Settings API functions one by one. Firstly, is the settings_fields($option_group) function which renders the hidden input fields and handles the security aspects (nonce etc.).

This function has only one parameter, $option_group, used for the options group name which must be a unique string. This string is also used as the first parameter of register_setting() and unregister_setting() functions (discussed below) and should be identical in all three functions.

Next we have the register_settings() function. It job is to register the option(s) being defined with the Settings API, and accepts three parameters. The first one is the group options name which is the same as in the settings_fields() function. You can define different sets of group options with this option should you choose to but most often you will likely use only one option group.

The second parameter is the name of the option(s) you are registering which is the name used to store the options in the options db table. The option name can be a reference to a single option or an array of options. It is considered good practice to group all of your options into one option variable (i.e. all your individual options are stored in an array) which reduces the queries to the database.

The unregister_settings() function is very similar to the register_settings() function except that it unregisters (not surprisingly!) the option(s) in question from the Settings API.

Moving on, next we have the add_settings_section($id, $title, $callback, $page) function. This creates the ‘section’ for the options you will be adding. You can have more than one section on an options page but usually for most pages you will probably only need one. This time we have four parameters which are quite straightforward.

First is the section ID, which must be unique. Then we have the title of the section that is displayed on the options page. This is followed by the callback function that displays the actual HTML of the section (such as introductory information, perhaps donation links etc.). Finally we have the page parameter which needs to be the same string as specified in the add_settings_field(), and do_settings_sections() functions (covered below).

Next we have add_settings_field($id, $title, $callback, $page, $section, $args = array()), which is closely relate to the add_settings_section() function. This function adds the option field to the newly created section, and you will need to place a call to it in your code for every new option you wish to add. So, if you have three textboxes and two checkboxes on your options page you will need a total of five calls to the add_settings_field() function. Also it is worth mentioning that the order in which you place the calls to add_settings_field() will determine how the options are rendered on the options page. To re-order the options simply swap the function calls around! How easy is that?

The first four parameters are almost identical to the previous function where you specify a unique ID for each field added to the Settings API, give it a field title that is displayed on the page, a callback function that actually renders the field, and a page – which must be the same one specified in the add_settings_section() function. The fifth parameter specifies the ID of the settings section. This needs to be identical to ID you entered for the first parameter of add_settings_section() previously. Finally, the last parameter allows you to add additional arguments.

I think it is worth mentioning here that the ID used in the first parameter of the add_settings_field() function does NOT have to match the ID for the input field HTML tag specified in the callback function. Most of the time it is advisable to do so to keep things simple but, as you will see later on for radio buttons, the ID’s for the input field HTML tags can be different.

I initially thought that they had to be identical for everything to work properly but after experimentation this does not appear to be the case at all. This only seems to be useful when adding radio button options to your plugin – as it makes no logical sense to have multiple HTML input tags with identical ID’s. For other controls I would strongly suggest (for simplicity) that you match the add_settings_field() ID to the callback function HTML input field tag ID.

Finally we have the do_settings_sections($page) function. This function handles the rendering loop which displays all of your option input fields to the form on the options page. It has only one parameter, $page, which must be the same as specified in the last two functions: add_settings_section(), and add_settings_field().

OK, so that is all the major Settings API functions covered. We will now discuss the various input field types that can be added to options pages and how you can use these with the settings API. There are various input field types that you can use on your options pages (checkbox, radio button, dropdown box, textbox, password box, and textarea). To add each one requires the correct integration with the Settings API. Basically the callback function you specified in the add_settings_field() function is where you place HTML code to render the options. Let’s now go through all the input field types and see an example callback function for each option control type.

Drop Down Box

The code to add a drop down combo style box is:

function  setting_dropdown_fn() {
	$options = get_option('plugin_options');
	$items = array("Red", "Green", "Blue", "Orange", "White", "Violet", "Yellow");
	echo "<select id='drop_down1' name='plugin_options[dropdown1]'>";
	foreach($items as $item) {
		$selected = ($options['dropdown1']==$item) ? 'selected="selected"' : '';
		echo "<option value='$item' $selected>$item</option>";
	echo "</select>";

Here, we basically get the current state of the option from the db, then loop through the available color options. If we find the one that is selected then we render it as the selected item otherwise it is added to the list of available options (but not selected). This good thing about this method is that it is nice and scalable for any number of options.


The code to add a textarea control is:

function setting_textarea_fn() {
	$options = get_option('plugin_options');
	echo "<textarea id='plugin_textarea_string' name='plugin_options[text_area]' rows='7' cols='50' type='textarea'>{$options['text_area']}</textarea>";

Nice and easy this one. Just get the current value (if any), and add it to the textarea!

Textbox (single line)

The code to add a textbox control is:

function setting_string_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_string' name='plugin_options[text_string]' size='40' type='text' value='{$options['text_string']}' />";

Another easy this one. Again, just get the current value (if any), and add it to the textbox!

Password Textbox (single line)

The code to add a password textbox control is:

function setting_pass_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_pass' name='plugin_options[pass_string]' size='40' type='password' value='{$options['pass_string']}' />";

This one is pretty much identical to the textbox except the input is masked.


The code to add a checkbox control is:

function setting_chk1_fn() {
	$options = get_option('plugin_options');
	if($options['chkbox1']) { $checked = ' checked="checked" '; }
	echo "<input ".$checked." id='plugin_chk1' name='plugin_options[chkbox1]' type='checkbox' />";

To add a checkbox we need to toggle the setting each time it is selected (and if the Save Changes option button has been clicked). For each checkbox we check the db option setting to see if it has been selected/deselected, and if so then the control needs to be updated.

Radio Buttons

The code to add radio button controls is:

function setting_radio_fn() {
	$options = get_option('plugin_options');
	$items = array("Square", "Triangle", "Circle");
	foreach($items as $item) {
		$checked = ($options['option_set1']==$item) ? ' checked="checked" ' : '';
		echo "<label><input ".$checked." value='$item' name='plugin_options[option_set1]' type='radio' /> $item</label><br />";

To add radio buttons you obviously need more than one, or there isn’t much point to using them! So, you need to define a ‘set’ of buttons. Here I have created an example of a set of shapes. The current db option is obtained as usual and stored in $options. Similar to the drop down box, each possible option is looped through until the db selected option is encountered. The db item is rendered to the screen as selected, and the other options are just displayed unchecked.

OK, enough theory.. Now for the REALLY good stuff – the tutorial! The following section covers a complete walk through on how to add options to a new page under the Settings Menu. The full code is given at the end for you to copy/paste into your own plugins as a starting template.

Tutorial – Adding a New Options Page

OK, let’s build a complete options page using the new Settings API armed with all the theory we have learned so far. First though, a couple of notes. All the callback functions have been added with a suffix of '_fn' to denote it refers to a callback function. There are a lot of parameters flying around so I think it is useful to know just at a glance what is a callback function and what is an ID etc. Also, the tutorial uses the method of storing all page options in a single options db table entry, which is a cleaner way to add your options to the WordPress db.

You will see the PHP ‘magic constant’ __FILE__ pop up a fair bit. All this fancy looking variable does is use the plugin file name as a constant in a place where a unique string is needed. __FILE__ is considered the correct constant to use in these circumstances as (hopefully!) your plugin name should be pretty much unique compared to the other plugins under your WordPress installation. Also, it has the added advantage that it saves us from having to come up with unique page names.

The first thing we need to do is create our sub menu page under the top level Settings Menu. This is done by using the add_options_page('Options Example Page', 'Options Example', 'administrator', __FILE__, 'options_page_fn') function. Here the parameters (in order) are: page title (displayed in the browser title bar), options menu title (displayed as a link in the Settings Menu), access level (who can access the options page), unique page name, and callback function to display the options form. The code to add the new menu page plus action hook is:

add_action('admin_menu', 'sampleoptions_add_page_fn');
// Add sub page to the Settings Menu
function sampleoptions_add_page_fn() {
	add_options_page('Options Example Page', 'Options Example', 'administrator', __FILE__, 'options_page_fn');

While we are at it let’s define the options_page_fn() callback function:

function options_page_fn() {
	<div class="wrap">
		<div class="icon32" id="icon-options-general"><br></div>
		<h2>My Example Options Page</h2>
		Some optional text here explaining the overall purpose of the options and what they relate to etc.
		<form action="options.php" method="post">
		<?php settings_fields('plugin_options'); ?>
		<?php do_settings_sections(__FILE__); ?>
		<p class="submit">
			<input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e('Save Changes'); ?>" />

OK, so what is going on here? Well the nice thing about the Settings API is that it handles a lot of the tedious set up and rendering of your options form. The form is pretty standard apart from the two function calls inside the opening/closing form tags. Firstly is the settings_fields('plugin_options'); function which basically outputs all the hidden fields automatically (including all the security stuff – nonces etc.). Next is the do_settings_sections(__FILE__) function which handles the rendering of the form input field HTML tags (it works by cycling through all the defined add_settings_field() functions and executing each callback function in turn to output the HTML).

To complete the setup (yes it is nearly done!) all you need to do is register your settings, and define the fields you wish to add to your options page plus the callback functions that define the HTML rendering of the option fields. So let’s do that now.

We place all the code to register settings in a callback function that runs when the ‘admin_init’ hook fires. The code looks like this:

add_action('admin_init', 'sampleoptions_init_fn' );
// Register our settings. Add the settings section, and settings fields
function sampleoptions_init_fn(){
	register_setting('plugin_options', 'plugin_options', 'plugin_options_validate' );
	add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__);
	add_settings_field('plugin_text_string', 'Text Input', 'setting_string_fn', __FILE__, 'main_section');
	add_settings_field('plugin_text_pass', 'Password Text Input', 'setting_pass_fn', __FILE__, 'main_section');
	add_settings_field('plugin_textarea_string', 'Large Textbox!', 'setting_textarea_fn', __FILE__, 'main_section');
	add_settings_field('plugin_chk2', 'A Checkbox', 'setting_chk2_fn', __FILE__, 'main_section');
	add_settings_field('radio_buttons', 'Select Shape', 'setting_radio_fn', __FILE__, 'main_section');
	add_settings_field('drop_down1', 'Select Color', 'setting_dropdown_fn', __FILE__, 'main_section');
	add_settings_field('plugin_chk1', 'Restore Defaults Upon Reactivation?', 'setting_chk1_fn', __FILE__, 'main_section');

Here we need to register our settings with the Settings API, and we do this with the register_setting('plugin_options', 'plugin_options', 'plugin_options_validate' ) function. The first parameter is the option group name that identifies all of the options in the set. The second parameter is the options name that is stored in the options db. Finally the third function defines an optional validation function that can be used to validate user input (more on this later).

Next we define our section that will contain all our option fields: add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__). The parameters in order are: ID for the section, title (to be displayed on the options page), callback function (that displays section information), and finally – page (defined as the constant __FILE__).

After the section definition we now define the individual fields themselves. Each option on your form needs to have a call made to a add_settings_field('plugin_text_string', 'Text Input', 'setting_string_fn', __FILE__, 'main_section') function. The parameters are similar to the add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__) function. They are specified as: field ID, title of field (displayed on the left hand side of the options page), callback function (to render the input field), page name (__FILE__ constant again), and finally a reference to the section name (same as the first parameter in the add_settings_section function).

Each callback function in add_settings_field() needs to be defined. We showed all the possible types earlier and so for completeness lets just show the callback function here for the field defined above:

function setting_string_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_string' name='plugin_options[text_string]' size='40' type='text' value='{$options['text_string']}' />";

Now we know how to structure the options page setup lets look at another couple of important points. Firstly, how do we specify default options? Also, how do we use the validate callback function defined in register_settings(). Firstly the default options…

To set these up we need to specify a hook that runs when the plugin is activated on the plugins page. This will trigger a callback function to be executed (which we specified in the hook definition). The callback function will then add the required defaults to the options db, as follows:

register_activation_hook(__FILE__, 'add_defaults_fn');
// Define default option settings
function add_defaults_fn() {
    $arr = array("dropdown1"=>"Orange", "text_area" => "Space to put a lot of information here!", "text_string" => "Some sample text", "pass_string" => "123456", "chkbox1" => "", "chkbox2" => "on", "option_set1" => "Triangle");
    update_option('plugin_options', $arr);

Taking this one step further, it would be nice if we could specify one of our options as a switch to determine if we ALWAYS want the options db entries reset every time the plugin is first activated (or whenever the plugin is deactivated/reactivated), or whether to leave them as they were as sometimes you will want to temporarily want to deactivate a plugin but not delete/reset the options. This way, the next time you activate the plugin the options will still be there. Anyway, it is a nice feature to give to your users. The following modified add_defaults_fn() function handles this:

function add_defaults_fn() {
	$tmp = get_option('plugin_options');
    if(($tmp['chkbox1']=='on')||(!is_array($tmp))) {
		$arr = array("dropdown1"=>"Orange", "text_area" => "Space to put a lot of information here!", "text_string" => "Some sample text", "pass_string" => "123456", "chkbox1" => "", "chkbox2" => "on", "option_set1" => "Triangle");
		update_option('plugin_options', $arr);

Now the options db entries only ever get overwritten if the checkbox option to restore the defaults is checked. Also, the very first time the plugin is activated then there will be no current options to overwrite and this is where the second test in the line if(($tmp['chkbox1']=='on')||(!is_array($tmp))) comes in. Basically, if there is no options in the db then it creates them with default settings.

OK, deep breath, nearly done. Finally, it is worth showing you how to use the validation feature built in to the Settings API. If you remember this is specified as a callback function in the third parameter of the register_settings() function.

Note: Remember that if you define this callback function in register_settings() then you must define this function in code otherwise you may get unexpected results (as WordPress will not be able to find a function that it is expecting). The first time I specified a validation function I forgot to implement it and it took me a while to realise what I had done wrong, and why the options form was not displaying properly!

The validation feature is a really nice touch of the Settings API and is worth using, it can be very powerful too. As a simple example, for the textbox we defined, how would you like a super easy way to validate input so that no HTML tags were allowed. If any are found then they are simply striped out! Nice eh? The code to do this is as follows:

function plugin_options_validate($input) {
	// Check our textbox option field contains no HTML tags - if so strip them out
	$input['text_string'] =  wp_filter_nohtml_kses($input['text_string']);	
	return $input; // return validated input

This is especially nice as we can test some/all of our options and validate them as we see fit. In the above example we are only testing the text field but we can easily extend this and add whatever validation code we like!

OK, let’s now add the rest of our input field options as we defined earlier in the post. Putting everything we have learned together, the finished options page looks something like the image below.

The full code to render the complete options page can be found here. To test this in your own WordPress installation simply copy/paste the code into a new text file, save it and upload as a new plugin. You can then begin to experiment and build your own options pages from this example template. Enjoy..! :)

In Summary

In this post we looked at the Settings API available in WordPress which makes it much(!) easier to add/maintain options for your plugins. The tutorial showed an example of adding a new sub page to the Settings Menu. Adding new options to existing menu pages as well as adding new sub pages to other top level menus (such as the Tools Menu, and Appearance Menu) are fairly straight forward. These will be covered in another post on the Settings API.

I hope you have found this post helpful, if so I would love to hear your comments…

You can also follow me on twitter.

Below are some helpful links I came across whilst compiling this post:

117 Responses to Settings API Explained

  1. Pingback: Leading Traceability Systems Integrator BaxTek Solutions Partners with LXE | Droid Reviews

  2. great share, great article, very usefull for me…thank you

  3. dgwyer says:

    Thanks, your welcome! I’m planning to do another one soon on using the Settings API to structure your options pages but specifying the form table yourself rather than let the Settings API take care of it. The advantage being that this approach gives you much more control over the final layout.

    • Paul Kaiser says:

      Hey DG,
      Great post – Settings API is great, and with your help I’m using it.
      Do you know how we can use the Settings API with built-in WordPress meta-box functions?
      I need to use the built-in Category selection box on my plugin options page, but can’t see a way to do it with the SAPI.
      Take care.

      • dgwyer says:

        Hi Paul, I will be doing a post covering rolling-your-own options page layout using the settings API. This will probably include how to add meta boxes as it is pretty useful to be able to do this. Not sure when though, as I am pretty busy!

        Also, using jQuery you have other options over using meta boxes. See here for an example of using the jQuery accordion control in your Plugin options page (great for squeezing lot’s of options into one page!):

  4. good share, great article, very usefull for us…thank you

  5. Steve says:

    This was a great tutorial and really helped me out. The only issue I’m having is that the defaults are not working. I using WP 3.0 with Multi-site.

    Any help would be appreciated.


  6. dgwyer says:

    Hi Steve,

    Thanks for the comment, glad it helped your understanding. :)

    Could you elaborate a bit on your problem? What specific defaults are not working? All of them? I would suggest that (if you haven’t already) made sure you get things working on WP 2.9.2, or WP 3.0 with MU not enabled to make sure he problem is with your options set-up and not a conflict with MU.

  7. Dave says:

    Does unregister_setting remove the saved options from the database? And what is the callback used for?

  8. dgwyer says:


    I would assume that the unregister_setting() function removes the plugin options db entries. This is logical since (via the register_settings() function) the Settings API handles the creation/updating of db options, it would follow that unregister_setting() removes options and cleans everything up.

    I must confess that in my recent plugins I have been using the delete_option() function with the register_uninstall_hook, to explicitly remove db options when the plugin is deactivated and removed by the user.

    As for the callback function in unregister_setting() I think this just removes the filter that calls the sanitize function for the option, so it is not called any more (since you want to unregister the setting).

    You can view the full function here (line 1498):

    Hope that helps


  9. Marcel says:

    This was an immense help, thanks for that.

    You wouldn’t know if something like the Settings API exists for user metadata as well; or do I have to do all the saving/retrieving/updating/type checking et cetera of user metadata myself?


  10. dgwyer says:

    @Marcel As far as I know there is no User API or equivalent but is a good idea I think! For know you will have to handle user meta info manually I guess.

  11. Pingback: » Blog Archive » Settings API Explained | Press Coders

  12. Jonathan says:

    Thank you David for the excellent tutorial; it’s a great jumping off point. I was able to expand on your sample code with another post ( to create a theme options page that also uses WP Meta Boxes (not terribly useful, but interesting).

    Now if only I could find a tutorial that thoroughly explained the WP Media API 😉


  13. dgwyer says:

    @ Jonathan Your welcome, thanks for the feedback :) Glad it helped. I plan to do many more similar articles, time permitting. At the moment I am kind of in a spin trying to move forward several development projects for this site including more plugins, WordPress applications, and of course some new WordPress 3.0 ready themes.

    If only there was more time to do everything!

    Seriously though, there is some excellent information on the Codex but it is not always clear what are the best (correct) ways to go about things in the LATEST versions of WordPress (as they are released). To address this I plan to aggregate information I post in articles etc. into a html/pdf document that focuses only on the latest version of WordPress and includes best practices for themes and plugins.

    Access to this would be free to begin with, and as this grew into a larger document there would probably be a small fee for subscription access to the new version (to cover development costs etc.). The document would be constantly updated with changes to the previous version clearly stated). I would be open to collaboration from other authors, perhaps who are experts in particular areas of WordPress. I would definitely use such a document myself for constant reference but depends on if enough people want to developed. Otherwise it will be put on the back burner for now.


  14. WP Themes says:

    Amiable brief and this mail helped me alot in my college assignement. Gratefulness you on your information.

  15. Dana says:

    Great stuff. Thanks! Question for you: How would you approach having a file upload box in your options page? When I get redirected back from options.php, my $_FILES var is empty.

  16. Asaf Chertkoff says:

    I’m trying to do a multiple select box that will save multiple options,
    and i’m having truble.
    any advise?


    • dgwyer says:

      Using one or more select boxes in your options page(s) should not pose a problem if you follow the appropriate methods outlined above. :)

      • Asaf Chertkoff says:

        I Think you misunderstood me…
        i worte “multiple select box” and not “multiple select boxes”.
        there is an attribute of the tag that called multiple. the syntax is

        it gives the possibility to check multiple options as selected=”selected”.
        the problem is that i don’t know how to use it with the settings API.

        the regular select input looks like this:

        while the multiple select box looks like this:

        i want to use it for displaying posts (with get_posts) in the select box, and save three of them as selected (to the options as it is done with the setting API) , instead of just one as the select box in the select box in this tutorial gives.

        a leed maybe…?


        • dgwyer says:

          You are talking about a combo box. And I guess there should be no problem at all implementing this using the settings API. Should be pretty easy.

          I have not coded an options page with the combo control but it would probably be along the lines of the checkbox (as that allows multiple selections). I would suggest you study the above implementations and that will give you a good starting point.

          It shouldn’t be that hard to figure out.

        • mtschirs says:

          A multiple select box:

          An apple
          An egg
          A stone

          plugin_options[‘your_possessions’] = array(0 => ‘apple’, 1 => ‘stone’);

  17. Asaf Chertkoff says:

    The syntax is “”.


  18. Phillip says:

    Thanks for this article. It cleared up a lot if questions I had.
    One issue that needs correcting or clarification.
    Your explanation of the register_setting() function states: basically a wrapper around the WordPress add_option_update_handler() function.
    My research shows that this is not the case. The “add_option_update_handler() function is deprecated as indicated by the following two links. (see Deprecated section to the left.)

    The register_setting() function lives in wp-admin/includes/plugin.php line 1475 makes no mention of add_option_hander() function.

    I may have missed something or my research may be incomplete. If you have more information, will you please clarify and support your statement.

  19. dgwyer says:

    Hi Phillip,

    Thanks for the comments.

    As to your query, the article was written pre WordPress 3.0, version 2.9 in-fact. The new register_setting() function is now basically the old add_option_hander() function rather than being a wrapper around it. That is why the add_option_hander() function is now deprecated.

    Check out for yourself the functions in WordPress 2.9 and 3.0.1, in the wp-admin/includes/plugin.php here:

    I have updated the post accordingly, thanks for the query I was not previously aware of the change. But that’s WordPress for you, constantly changing, evolving, getting better by the day! :)

  20. Phillip says:

    dgwyer. Howdy! and thanks again for this jumping off point. I’m about ready to jump of the roof! Just kidding. Actually I’m getting to know this API quite intimately and find it very challenging. I’ve thought a few times about just using the Options API as it seems much more simple. What do you think of the two side-by-side?
    Anyway, what I’m interested in discussing is the register_setting() function.
    First, the parameter, “Option Group”. Let’s say I want an option group related to each group of add_settings_field which would also be related to a single add_settings_section. Would I then need a new instance of the register_settings() function for each “option group” with the second parameter “option name” being the same through each instance? Now stay with me if you can (or want to). If the register_setting() function is able to handle multiple settings names, would that not call for a function with several if statements to find a option name in an array and apply the sanitation to that? Furthermore, should the option name in the register_setting() function be related to the id parameter in the add_settings_field() function as the field should be related to the new setting. And going even further, would I also need a settings_fields() line for each “options group” in the options form? I’m only theorizing here and wondering if you have had the extensive experience to verify what I’m posing? If not just tell me to go apply my theory to see if it works. If so please verify to save me some hair. Thanks for your time and again for the article.

  21. Toby says:

    Hi dgwyer
    Thanks for the post, WP drives me nuts with its lack of adequate documentation so posts like this help bring things together.

    Here are a few points I would like to add based on reading the code to figure out what is actually happening and to take a little of the voodoo out of creating a settings page.

    register_setting(group,option) adds an entry into the white listed options under the group name.

    settings_fields(group) adds three hidden fields to your form: an action field with the value “update”, a security nonce and “options_page” which contains the value passed in as the group. This must be called from within the form as it outputs directly with echo. This function has no other magic side effects, it simply adds these fields to your form.

    When the form is submitted the options_page value allows wp to retrieve the correct list of white listed options. Note that there can only be one group per page so the name of the group parameter might be better called “page”. However there is no link between the page slug and the group value so calling it group helps show it is distinct.

    The form must use “options.php” as its action (regardless of which settings page your page is a submenu of) as options.php does the saving of white listed options for the page given as group and then redirects back to the calling page.

    The add_settings_section(), add_settings_field() and do_settings_sections() functions are simply for drawing the form and have nothing to do with registration or validation of the inputs and can be omitted if you create your form manually or via other functions.

  22. Toby says:

    A couple of other things:

    The $args parameter for add_settings_field() is passed to your callback function so that you can give it additional info, but the callback does not receive any of the other parameters from the calle to add_settings_field().

    Also the unregister_settings() does not remove options from the table it simply takes them out of the white list. This means you will almost never need to use this function (it may be there to remove built in options as you are not going to call register_settings and then unregister_settings on your own options)

  23. As Toby mentioned, the callback does not receive any of the parameters from the call to add_settings_field() except the $args parameter. If I understand this correctly (which I’m not sure I do), it is nearly impossible to reuse a callback function without a bit of hacking. Is that correct? The only way around that limitation that I can see would be to pass the ID used when calling add_settings_field() as part of the $args parameter. Is there an easier way?

  24. Toby says:

    Hi Alex

    It is possible to right a reasonably generic callback method and then pass the necessary details in an associated array in the $args parameter. Usually the $args array will have to contain many of the same values you were passing into add_settings_field(). Personally I don’t bother with add_settings_field() and just make calls directly to my own generic field generating method.


    • dgwyer says:

      Same here, I don’t bother really with add_settings_field() or add_settings_section() in my day to day options pages. I find that rolling my own gives me much more flexibility.

      Those two functions are very useful though for people who don’t want to, or don’t know how to, write their own. They can use them to quickly create simple settings quickly and consistently.

  25. Admittedly, this is my first time really getting my hands dirty with the settings API. Are there many things that the add_settings_field() function does behind the scenes that would have to be rewritten manually if I decided to roll my own?

    • dgwyer says:

      The main advantage to rolling your own options is that you can set everything out in one go, so is very easy to see the whole thing. It is also easy to maintain, by simply dropping in new options. As I did more and more options pages I found the add_settings_field approach a little restricting and having all your options ‘hidden’ away in callback functions. It is a matter of preference really.

      If you want to pursue the roll your own method then check out my WP Content Filter Plugin with is on the repository here:

      It is a fairly simple, clean example of using the Settings API. I just love how easy it is to add new options, edit existing ones etc. I remember the nightmare it was to setup and maintain options before the Settings API was introduced! *Shudders*

  26. Keyamoon says:

    Great tutorial. I used it to make a settings/options page for my theme. I wanna add a little tip: If you are also going to use a theme’s functions.php file to add your settings, do not use the __FILE__ constant. Instead, you may want to use your theme’s name or some other unique name.

  27. Pingback: Adding Radios in Plugin Options Page of Admin?

  28. jwack says:

    Excellent post, written well enough a non-programmer like myself was able to use this to add a settings page to a plugin, thanks!

    Curious, in one of your comments above you said…
    “I’m planning to do another one soon on using the Settings API to structure your options pages but specifying the form table yourself rather than let the Settings API take care of it.”

    Did you ever do this? I would like to take a look if so.

    • David says:

      I haven’t done another tutorial as such, but if you look at my WP Content Filter Plugin, that gives you a full example of how to implement specifying the form table yourself.

  29. Great tutorial! You can simplify the code a little by using the checked() and selected() functions.

  30. David says:

    Thanks Stephanie!

    I have been meaning to update the article or do another (smaller!) tutorial on the Settings API for some time now, using the functions you mentioned. :-)

  31. Devin says:

    Hi David. Excellent post!

    I’ve been building off it the last couple hours to eventually roll an options framework ( into it. Instead of doing a separate callback functions for each setting, I’m passing another set of arguments so it can be build out programatically:


    add_settings_field(‘select_example_1’, ‘Select an Item’, ‘build_option’, __FILE__, ‘main_section’, array( ‘label’ => ‘Select Example’, id’ => ‘select_example_1’, ‘type’ => ‘select’, ‘description’ => ‘Select Description’, ‘std’ => array(1,2,3), ‘options’ => array(1,2,3))

    Then build_option($args) can use switch statements to generate all the needed code for each different item.

    I’ll post an update when I get it a little closer to completion. Just wanted to let you know this post was super helpful. Cheers!

  32. David says:

    These days I only usually build my options table in one single form rather than factoring it out into separate functions. I prefer this was as I can see the form as a whole, and find it easier to drop in new options as/when I need them.

    See my WP Content Filter for a pretty simple implementation of this. Also, I like to use jQuery these days (tabs/accordions) to use the available options screen space as efficiently as possible (rather than use multiple options pages.

    My Skype ID is: dgwyer71. If you feel like swapping notes on WordPress development any time, by all means add me for a natter sometime!

    That invitation goes for anyone else too.. :-)

  33. Pingback: Settings API Tutorial « rtCafe rtCafe

  34. Fitra Yuda says:

    Very usefull for me, thank you :)

  35. Rahe says:

    There is a function in WordPress to Check a dropdown or a checkbox is selected, it’s selected() and checked() function, try to search this on WordPress code 😉

  36. Pingback: Twitted by duffyblackpeace

  37. Drew says:

    Hey David,
    How would I actually get at the options stored? For example, how would I echo out the stored option for the text area?

    echo get_option(plugin_options[text_area]) ?

    Otherwise this is a complete tutorial, great work!

  38. Rutwick says:

    Dude, Long Live! Every time I make a plugin with options, and want to use the new settings API, your post saves my rearside! THANKS MAN! :)

  39. jamie says:

    Do you have to make a new function for every input? I am coding an admin and I am coding it into jquery tabs. it is working but i am now trying to add more than one option per tab. But instead of adding a new function for every option, I am trying to add multiple inputs per function. Can this not be done?

    • David says:

      For adding multiple options I tend to code the form ‘all in one’ rather than have a separate function for each option. This way you can see how the form is structured al in one place rather than in discrete functions. My WP Content Filter is a nice simple example of how this can be done.

      • jamie says:

        I must be missing something. I have my form all set up and it is being submitted with ajax and it is working well. But when I try to add a second text box, that is where the issue comes in. I cannot see any way to do it without adding another function and another settings field. I would have thought there is a way to make one function and then have a drop down, text box or whatever I want. The only way I can see it working is to make a whole new function for every input you want in your form. well, if I want 50 inputs, that would be 50 functions. There must be a better way to do it. I don’t see what I am doing wrong.

  40. David says:

    Yes there is probably a way to do that but I don’t bother. I just create the options form as you would any other form. I like this approach as you get the best of both worlds. You can use the Settings API, plus be able to layout your form in an intuitive way.

    Like I said above see my WP Content Filter Plugin for a really simple example of this that you can reuse. Otherwise if you dig around, or post on the WordPress support forums you may find what you are looking for.

    Not sure if this helps:

  41. elba says:

    Thanks for the tutorial/example. As I wp beginner I can’t figure out how to echo the settings saved in the option panel in a wp page. Could you post a short example how to render one of the fields data?

  42. Pingback: | Looking at WordPress Theme Options

  43. R D G says:


    I just found this and it was very helpful. The part that still doesn’t make sense to me is:
    register_setting(‘plugin_options’, ‘plugin_options’, ‘plugin_options_validate’ );
    It seems redundant to pass the same parameter to register_setting twice.

    Also it seems that when using the settings api you have to set the action of the form as follows
    to get it to work. But what if I want to execute my own php code when the form is submitted?

  44. mtschirs says:

    A multiple select box ( replace ( ) with ):
    (select name=”plugin_options[your_possessions][]” multiple=”multiple”)
    (option value=”apple” selected=”selected”) An apple (/option)
    (option value=”egg”) An egg (/option)
    (option value=”stone” selected=”selected”) A stone (/option)

    plugin_options[‘your_possesions’] = array(0 => ‘apple’, 1 => ‘stone’);

  45. Gaby says:

    Thanks for your post, helped me, I’m learning wordpress and I need to add a file to the multimedia gallery in my plugin, you could help me with that? thank you very much in advance ..

    • David says:

      Hi, Glad you found the post helpful. I’m afraid I’m pretty tied up with development work right now but you might want to try the WordPress support forums instead. :)

  46. jamie says:

    with setting up an admin area using this technique, do you need to create a new function for every field and then use the add_settings_field for every input you make?

  47. jamie says:

    oops, never mind. I already got this answer

  48. I don’t think I’ve seen this said in such a way before. You really have cleared this up for me. Thank you!

  49. sharath g says:

    Okay this tutorial worked great for me… BUT i have gotten stuck at a point.. Can someone help me out?

    I created a field called text_url to input URLs from users and it works perfectly fine.

    how do i call text_url field information?? Any ideas

    Thanks in advance for all the help!!

    • David says:

      How do you mean ‘call text_url field information’? I’m not sure exactly what you mean here. If you mean, how do I store and retrieve values entered in a text box then that’s pretty straightforward.

      I have created a Plugin options starter kit which is available to download via the Plugin repository. It contains examples for all the commonly used Plugin options. Check it out to see how to handle text boxes:

  50. Pingback: Give feedback on errors with the WordPress Settings API

  51. Pierre-Gilles RAYNAUD says:

    Very interesting and practical tutorial for WP beginners.

    In order to get this plug-in running, I had to replace, the PHP “macro” __FILE__ by an unique identifier (I”m using WP 3.1.2 and my own theme).

  52. Pingback: The WordPress Settings API Explained | ChurchCode

  53. rotsee says:

    I keep getting ”You do not have sufficient permissions to access this page.” when trying to access my plugin’s settings page that I added with add_options_page('My Plugin Options', 'My Plugin', 'administrator', __FILE__, 'options_page_function');. I alsa tried changing ”administrator” to ”manage_options” with no differences (I’m logged in as administator, Any suggestions?

    • rotsee says:

      Now this is funny: Replacing __FILE__ with a hardcoded string everyhing works fine again. Seems a bit odd to me, but I’m really an amateur here, so I’m probably missing something obvious.

      • It’s funny indeed; the same actually happened to me! Deleting __FILE__ from everything but the register_activation_hook() made everything work flawlessly for me. I wonder if this is because I had a multisite install… or simply because WordPress 3.1.2 (and 3.2 Beta) have slightly changed the Settings API.

        Nevertheless, this remains an excellent, concise tutorial, which is very simple to follow. I finally got checkboxes working under the Settings API, which is something I never managed to do — yay! :)

  54. Pingback: Easy WordPress Plugin Development (Part 4) | WP Code Snippets

  55. Pingback: The WordPress Settings API Explained | Church Mag

  56. Ray says:

    Nice tutorial!

    Not a fan of calling get_option() for each settings field.

    Would be better if you globalized a variable and used get_option() once on admin_init, then you can just call the global when you need to refer to a particular setting.

    • David says:

      If you are trying to save calls to the database then you don’t strictly need to do that, as the call the get_option is usually cached so subsequent calls do not query the database all the time.

      I used to be concerned about how many times I was calling get_option throughout my Plugin and theme code until I found out get_option is cached by default. Just saves you the job of having to manually abstract the options data into a static global variable.

  57. newr says:

    Hi David, i’m totally new. how can you know the first paramenter for add_filter is “the_content”?
    What if i want to show the value in a div tag somewhere in the theme?
    thanks a lot master :)

  58. newr says:

    actually not only the right structure, almost the perfect stuffs to get started :)
    found this website database of wp hooks: #_#

  59. newr says:

    Hahaha this is funny, i tried creating test plugin like this:

    function test_add_options_page() {
    add_menu_page(‘Test Page’, ‘TEST’, ‘publish_posts’, __FILE__, ‘test_render_form’);

    i wanted, a user role author to be able to click and update the form but wordpress then says “Cheatin huh?” ..

  60. Mary B. says:

    Thank you very much for providing the Plugin Options Starter Kit. My very first experience with WordPress coding began in early May this year, when a client requested that I install and help her get a WordPress site up and running. Along the way, she decided that she wanted a links directory with considerably more functionality than the default “Links” in WordPress provided. Using your plugin as a starting point, I was able to write a links directory from scratch that does everything my client desires it to do. Your plugin saved me a ton of research time, and I appreciate that!


    • David says:

      Thanks for taking the time to comment Mary. That’s exactly why I wrote the starter kit Plugin, to give others a head start on creating Plugin options. Glad it helped you out. :)

  61. Alex says:

    Hi guys! I’m putting together a Theme Option page and I’m done with the Google analytics section:

    function google_analytics_setting() {
    $options = get_option(‘plugin_options’);
    echo ” {$options[‘google_analytics’]} “;

    but the source code it’s showing only the no analytics. The funny thing is that if I move the function call anywhere out of the footer.php

    it works fine. Any help? Thanks.

  62. Have you ever thought about writing an ebook or guest authoring on other sites? I have a blog based on the same ideas you discuss at and would love to have you share some stories/information. I know my subscribers would appreciate your work. If you are even remotely interested, feel free to shoot me an e mail.

  63. David says:

    I have been REALLY busy the last 6 months or so developing some cool themes with Scott Bolinger (Press Coders partner and graphics designer), but there are definitely lots more WordPress posts such as this one coming in the near future!

    As for e-books we haven’t ruled that out but will happen later rather than sooner.

  64. jellysandwich says:

    Hey David,

    I just got a copy of the starter kit, and I noticed that there’s a small bug with escaping quotes. So for example,

    ‘rows’ and ‘cols’

    turns into

    \’rows\’ and \’cols\’

    (appears like this in database as well)

    What’s the best way to handle this so that quotes get escaped properly?

    • David says:

      I have just updated the Plugin (v0.16) to use the esc_html() function to properly escape the default textarea content.

      Download the updated version, and check the ‘Restore defaults upon plugin deactivation/reactivation’ box, and save. Then just deactivate/reactivate the Plugin and the quotes should now be properly escaped.

  65. Nice article! Only one thing I don’t like is that if you want 2 checkboxes you need all this code and the functions are identical only the name of the checkbox is different. Is there better way to do this? What if i need 10 checkboxes? Than I have 10 identical functions?

    add_settings_field(‘plugin_chk1’, ‘Checkbox 1’, ‘setting_chk1_fn’, __FILE__, ‘main_section’);
    add_settings_field(‘plugin_chk1’, ‘Checkbox 2’, ‘setting_chk2_fn’, __FILE__, ‘main_section’);

    function setting_chk1_fn() {
    $options = get_option(‘bvdn_options’);
    if($options[‘chkbox1’]) { $checked = ‘ checked=”checked” ‘; }
    echo “”;

    function setting_chk2_fn() {
    $options = get_option(‘bvdn_options’);
    if($options[‘chkbox2’]) { $checked = ‘ checked=”checked” ‘; }
    echo “”;

  66. Juan J says:

    Any tutorial about how to implement an upload image button?

  67. Tom Hermans says:

    Long time since I’ve seen such a good, detailed and well-thought article. Nice work. Am gonna look for more articles/tuts from you..

  68. jamie says:

    What I don’t understand about this method of doing things is do you need to create a new settings field and function for every single option?

    • David says:

      No, you don’t. See my answer a few replies above.

      Also, check out the ‘Plugin Options Starter Kit’ Plugin for some sample code to get you started.

  69. Stephen says:

    Great Article,

    I’ve been looking at various blogs all over the net that demonstrate adding an option page and explaining Settings API, but your post is the best I’ve come across so far.

    Plus, you give the source code for your explain, which is reusable and ready to go for any project!

    Thank you!


  70. Gerard says:

    Thanks so much for this tutorial. Took me a while to get my head around all of this, but I got there eventually.

    Thanks again

  71. jkk says:

    Hi David, thank you for your tutorials.

    And I have a question regarding the following error when I use the code for new theme. Here is the errors:

    — Debug: Undefined index: text_string
    — Debug: Undefined index: chkbox2
    — Debug: Undefined index: dropdown1
    — Debug: Undefined variable: checked

    I am not sure why, but i suspect it’s related to the default settings. How can I fix.

    Other question is how can I create Tabs at Options page using Settings API.

    Thanks in advance.

    • David says:

      That may be because you need to save the options, to update the db settings.

      Using jQuery UI components such as Tabs and Accordions etc. are not really anything to do with the Settings API it is purely jQuery. However the two can be integrated without too much trouble.

      I have seen a few tutorials out there covering this topic. Just try a Google search for “WordPress Plugin options tabs”.

  72. khmamed says:

    thanks for the tuto

    i want to know how can i get the data and put it to my table in sql .

    in other hands i create table in db of wordpress and i get data from api setting page and i put it to my table
    thank you for responding

    • David says:

      That’s something I have never tried to do, so don’t really have any thoughts on that. Sorry.

      I always use the default options table to store Plugin/theme settings.

  73. Josh says:

    David, Thank you so much for a wonderfully written tutorial.

    I have one question when using the dropdown select box. I have successfully created my box, with my correct options, and everything saves perfectly.

    However, I would like to set up an “if” statement or a “switch statement” to run through the available options. Once the selected option is found, I would like to execute the code specific to that option.

    Basically, I’m hooking stylesheets to the tinymce editor. One makes the editor buttons green, one makes them pink, etc…

    So, how do I create a variable which will hold the item selected, so I can match it to my if statement?

    Thanks for any help!

    • David says:

      In your theme code just grab the drop down option and store in a variable, then run through the different options using a switch/case statement.

  74. Josh says:

    David… you are too prompt!!

    Okay, so here is what I have…

    if (($options['tinycolor']==$item)=="Green") {

    I tried setting this to something like…

    $myVariable = ($options['tinycolor']==$item)

    But it didn’t like it very much.

  75. Josh says:

    Here is what I’m currently working on:

    Perhaps you could take a quick look? The problem is that it will load the “green” stylesheet everytime, regardless of which item I have selected in the dropdown.

  76. David says:

    Something like:

    if (($options[‘tinycolor’]==”Green”) {
    // code here..

    Should work OK. Just echo out your variables to make sure you are testing for the right values.

  77. Andy Walpole says:

    Is it possible to decouple the Settings API from the Options API? Can I use the form creation and validation hooks to update a custom table in the database?
    From what I can understand (just started learning WordPress development last week) is that the two are bound together

  78. Joey the Squid says:

    Thanks for the great tutorial. I’m trying to put together a way to capture general contact information for the website owner/business in my theme. Using your step-by-step process I was able to successfully create a “Contact Information” admin page and it properly stores data such as the email address, fax and phone number.

    The problem I’m having is that I’d like to be able to call up the values of each of these settings using a shortcode such as [email] or [fax] or [phone] within the WordPress loop. Any advice on this?

    If it’s helpful here’s my function to register the settings:

    function contactinfo_init_fn(){
    register_setting(‘plugin_options’, ‘plugin_options’, ‘plugin_options_validate’ );
    add_settings_section(‘contact_details’, ‘contact Details’, ‘section_text_fn’, __FILE__);
    add_settings_field(‘contact_information_name’, ‘contact Name’, ‘setting_name_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_address1’, ‘Address’, ‘setting_address1_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_address2’, ‘Address Line 2’, ‘setting_address2_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_city’, ‘City’, ‘setting_city_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_state’, ‘State’, ‘setting_state_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_zip’, ‘Zip Code’, ‘setting_zip_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_phone’, ‘Phone Number’, ‘setting_fax_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_fax’, ‘Fax’, ‘setting_fax_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘contact_information_email’, ‘Email’, ‘setting_email_fn’, __FILE__, ‘contact_details’);
    add_settings_field(‘plugin_chk1’, ‘Restore Defaults Upon Reactivation?’, ‘setting_chk1_fn’, __FILE__, ‘main_section’);

    • Joey the Squid says:

      I think I solved my problem. I’m registering the short codes like this and they appear to be working:

      function contactphone_func($atts) {
      $options = get_option(‘plugin_options’);
      return “{$options[‘phone_string’]}”;
      add_shortcode(‘contact_phone’, ‘contactphone_func’);

      function contactfax_func($atts) {
      $options = get_option(‘plugin_options’);
      return “{$options[‘fax_string’]}”;
      add_shortcode(‘contact_fax’, ‘contactfax_func’);

      Does this look right to you?

  79. Dan Brown says:

    Wow thank you.

  80. Kaushik says:

    Best options tutorial so far on net! Thanks a ton

  81. Rock says:

    This is awesome! A little late to the party, but this is exactly what I was looking for to understand this aspect of writing a plugin. One question though, I want to be able to save a regex string, and it is not saving correcting using a text field on the plugin. How would I correctly save ‘|[^\s!\?&\\/”+-.\w\’\’–]|’