The Complete Guide To The WordPress Settings API, Part 5: Tabbed Navigation For Your Settings Page

The Complete Guide To The WordPress Settings API, Part 5: Tabbed Navigation For Your Settings Page

Tutorial Details
  • Program: WordPress
  • Version: All
  • Difficulty: Advanced
  • Estimated Completion Time: Varies
This entry is part 5 of 8 in the series The Complete Guide To The WordPress Settings API

At this point in the series, we’ve taken a close look at the Settings API and what it has to offer. We’ve even begun creating our own theme to help demonstrate everything we’ve been learning. We’ve covered sections, fields, settings, menus, pages, and more.

If you’ve been following along from the beginning, you’ve likely noticed that these articles are long and are code intensive. We’ve hit the major points of the Settings API so, for the remaining articles, we’re going to be taking a shorter, more focused approach on the rest of the topics. This will reduce the length of our articles and the amount of code we’re writing and hopefully make some of the ideas a bit easier to digest.

Last time, we left off in the middle of development: We’ve successfully created our own options page and introduced a few new options, but we left the project in a state that prevented all of our options from being properly saved. In this article, we’re going to take a look at why we’re unable to save our options and what we can do to fix it.

Before we get started: This article assumes that you’re familiar with the Settings API and theme options. If you’re a beginner or even intermediate WordPress developer, I highly recommend catching up on the rest of the series before diving into this post.


Why Won’t My Options Save?

If you’ve been following along through this series, your options page should look something like this:

Theme Options

Everything looks good, but there’s a problem with this setup – the “Social Option” values will properly save but the “Display Options” will not. Before going any further, it’s important to understand why we’re able to render our options out on a single page, but we’re unable to save both options.

Recall that earlier in the series, we defined two sets of settings for our theme – “Display Options” and “Social Options”. By using the Settings API, we’re telling WordPress to create entries for each group of settings in the database. Since we’ve defined two groups of settings, then two rows are created in the database. Next, the Settings API renders the options out to the dashboard using form elements. From there, WordPress takes the form values and saves them to the database.

In order to provide a greater level of security, WordPress assigns each group of settings a unique value called a nonce that protects against malicious attacks. Since a nonce value is applied to each group of settings, we’re currently rendering out a single form with two nonces. When you submit the form to the server, WordPress will only see (and, thus, use) the “most recent” nonce value. In our case, that’s the “Social Options”. As such, only those options are serialized – the “Display Options” are completely ignored.

This isn’t terribly advanced – in fact, you can actually see the two nonce values for each of our sections when you view the source of the page. Here is the nonce for the “Display Options:”

Theme Options

And here’s the nonce for the Social Options:

Theme Options

Your actual values will be different, but the input element will exist.

One way to prevent this problem from happening is to create a unique page for each group of settings. This isn’t a bad solution, but if you’re only working on a group of one or two options, creating an entire new page could be a bit overkill.

Luckily, WordPress supports middle-ground – you can still keep all of your settings on a single page, but ensure that users are able to save all of their settings and still have a pleasant user experience.


Enter Tabbed Navigation

You’ve no doubt seen tabbed navigation throughout the WordPress dashboard. Just take a look at the “Themes” page:

Themes

Tabbed Navigation provides a nice alternative for grouping sets of related options into a single page without sacrificing the overall user experience. This is what we’ll be implementing in the Sandbox Theme.

Before writing any code, it’s always a good practice to list out exactly what we’re going to do throughout development.

  • Introduce two tabs – one for Display Options and one for Social Options
  • Set the “Display Options” as the default tab when the page loads
  • Make sure that the same tab is marked as active after saving a specific page of options
  • Verify that the update message renders when settings are saved

Adding Individual Tabs

In functions.php, locate sandbox_theme_display. This is the function that we’re using to actually render the options page. As of now, it should look like this:

function sandbox_theme_display() {
?>
	<!-- Create a header in the default WordPress 'wrap' container -->
	<div class="wrap">
	
		<div id="icon-themes" class="icon32"></div>
		<h2>Sandbox Theme Options</h2>
		<?php settings_errors(); ?>
		
		<form method="post" action="options.php">

			<?php settings_fields( 'sandbox_theme_display_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>	
			
			<?php settings_fields( 'sandbox_theme_social_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_social_options' ); ?>	
		
			<?php submit_button(); ?>
			
		</form>
		
	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

First, let’s introduce our two tabs. This is relatively straightforward as we’re going to take advantage of CSS classes that WordPress already provides – namely, nav-tab-wrapper and nav-tab. In the sandbox_theme_display function, drop the following block of HTML just below the call to settings_errors():

	<h2 class="nav-tab-wrapper">
		<a href="#" class="nav-tab">Display Options</a>
		<a href="#" class="nav-tab">Social Options</a>
	</h2>

Obviously, this is very basic but we’ve just introduced two styled tabs that we’ll be using throughout the rest of the tutorial. At this point, your code should look like this:

function sandbox_theme_display() {
?>
	<!-- Create a header in the default WordPress 'wrap' container -->
	<div class="wrap">
	
		<div id="icon-themes" class="icon32"></div>
		<h2>Sandbox Theme Options</h2>
		<?php settings_errors(); ?>
		
		<h2 class="nav-tab-wrapper">
			<a href="#" class="nav-tab">Display Options</a>
			<a href="#" class="nav-tab">Social Options</a>
		</h2>
		
		<form method="post" action="options.php">

			<?php settings_fields( 'sandbox_theme_display_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>	
			
			<?php settings_fields( 'sandbox_theme_social_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_social_options' ); ?>	
		
			<?php submit_button(); ?>
			
		</form>
		
	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

And your settings page should look like this:

Initial Settings Tabs

Bringing the Tabs to Life

In order to begin toggling our options pages, we’re going to need to provide some type of signal or flag for which options we want to render. This can be done using a query string variable that identifies which tab was clicked and that can, in turn, be read using PHP.

So let’s ago ahead and give each anchor that we created above a unique flag that signals what tab we’re trying to load. Update your markup to look like this:

	<h2 class="nav-tab-wrapper">
		<a href="?page=sandbox_theme_options&tab=display_options" class="nav-tab">Display Options</a>
		<a href="?page=sandbox_theme_options&tab=social_options" class="nav-tab">Social Options</a>
	</h2>

Pay close attention here so not to miss this: We’ve provided two query string variables in each link – the page value and the tab value. The page value is necessary because it’s generated by WordPress via the Settings API and is used to tell the application which options page to load. The second value is an arbitrary value that we’ve used to signal which tab we’re on. Permitting you’ve done this correctly, notice that your browser’s address bar should reflect the values as you click on each tab.

Next, we need to write a little bit of PHP that reads the new query string value. Ultimately, this code is what will allow us to toggle our options page, but we’re going to take this a step at a time. So, let’s begin by writing a conditional to check to see if the query string value is set and, if so, store it in a variable. This can go directly above our nav-tab-wrapper that we’ve defined above.

<?php
if( isset( $_GET[ 'tab' ] ) ) {
	$active_tab = $_GET[ 'tab' ];
} // end if
?>

WordPress provides a class named nav-tab-active that we can apply to our anchor tabs to style them as active. As such, our next step will be to compare the value of the $active_tab variable to the tab query string variable and then apply that class name to the relevant tab.

To do this, update your code to look like this:

	<h2 class="nav-tab-wrapper">
		<a href="?page=sandbox_theme_options&tab=display_options" class="nav-tab <?php echo $active_tab == 'display_options' ? 'nav-tab-active' : ''; ?>">Display Options</a>
		<a href="?page=sandbox_theme_options&tab=social_options" class="nav-tab <?php echo $active_tab == 'social_options' ? 'nav-tab-active' : ''; ?>">Social Options</a>
	</h2>

Here, notice that we’ve written some inline PHP in the class attribute of each anchor. Essentially, the code says “If the active tab variable’s value is ‘display_options’, then echo the nav-tab-active keyword; otherwise, don’t echo anything”. Easy enough, right? Test it out a few times – you should see each of your tabs toggling back and forth.

At this point, your function should look like this:

function sandbox_theme_display() {
?>
	<!-- Create a header in the default WordPress 'wrap' container -->
	<div class="wrap">
	
		<div id="icon-themes" class="icon32"></div>
		<h2>Sandbox Theme Options</h2>
		<?php settings_errors(); ?>
		
		<?php
			if( isset( $_GET[ 'tab' ] ) ) {
				$active_tab = $_GET[ 'tab' ];
			} // end if
		?>
		
		<h2 class="nav-tab-wrapper">
			<a href="?page=sandbox_theme_options&tab=display_options" class="nav-tab <?php echo $active_tab == 'display_options' ? 'nav-tab-active' : ''; ?>">Display Options</a>
			<a href="?page=sandbox_theme_options&tab=social_options" class="nav-tab <?php echo $active_tab == 'social_options' ? 'nav-tab-active' : ''; ?>">Social Options</a>
		</h2>
		
		<form method="post" action="options.php">

			<?php settings_fields( 'sandbox_theme_display_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>	
			
			<?php settings_fields( 'sandbox_theme_social_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_social_options' ); ?>	
		
			<?php submit_button(); ?>
			
		</form>
		
	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

But wait – there’s a subtle bug in this code! Recall that when a user lands on the settings page the first time, there’s no value for tab in the query string. As such, we need to set one as the default. To do this, let’s update the conditional that checks for the presence of the query string variable. While we’re at it, let’s consolidate it using the ternary operator:

	$active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'display_options';

This says “if the query string contains a value for ‘tab’, assign it to the active tab variable; otherwise, assign the value of ‘display_options.’” This is exactly how we set the display tab as active. Once again, try out your tabs.

Toggling Our Settings Page

We’re almost done! The last thing that we need to do is to toggle our settings page based on which tab is active. Specifically, we only want to show the display options when the display tab is selected (and the same for our social options).

Since we have everything stored in the active_tab variable, we should be able to wrap our Settings API calls in a conditional and be good to go. So, first, locate the following block of code in your theme:

	<form method="post" action="options.php">
		
		<?php settings_fields( 'sandbox_theme_display_options' ); ?>
		<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>
		
		<?php settings_fields( 'sandbox_theme_social_options' ); ?>
		<?php do_settings_sections( 'sandbox_theme_social_options' ); ?>
		
		<?php submit_button(); ?>
		
	</form>

Notice that we have two calls to settings_fields and do_settings_section. Basically, we only want to render a single group out when a particular tab is selected. To do this, we simply write a conditional that checks the value of $active_tab and then runs the appropriate section:

	<form method="post" action="options.php">
		<?php
			
			if( $active_tab == 'display_options' ) {
				settings_fields( 'sandbox_theme_display_options' );
				do_settings_sections( 'sandbox_theme_display_options' );
			} else {
				settings_fields( 'sandbox_theme_social_options' );
				do_settings_sections( 'sandbox_theme_social_options' );
			} // end if/else
			
			submit_button();
			
		?>
	</form>

Refresh your options page – permitting you’ve done everything correctly, each group of settings should toggle based on the field and all of your options should properly save.


Conclusion

Tabbed Navigation is an easy way to group related options together and give your users a solid user experience by not inundating them with options. It’s relatively easy to implement and goes a long way to tightly integrating your options with the native WordPress look and feel.

In the next post, we’ll build on this even further by exposing a top-level menu that will make your theme options accessible via the menu along the side of the WordPress dashboard.


Other parts in this series:The Complete Guide To The WordPress Settings API, Part 4: On Theme OptionsThe Complete Guide To The WordPress Settings API, Part 6: Menu Pages
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.wpfix.org Wpfix

    Nice wp tutorial thanks for sharing this.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Sure thing!

  • http://www.vaultstudios.co.uk Martin Harvey

    What a great series this has turned out to be. I think that these tutorials will be the ‘come to’ resource for creating theme options pages. Hopefully showing everyone that settings API is you r friend and keeping everything in the admin pages of WordPress looking native is definately the way to go.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Thanks Martin – that’s been the goal all along. We’ve got a few more articles to go to round out the series, too!

  • http://profiles.wordpress.org/users/stephenh1988/ Stephen Harris

    Nice. As has been pointed out – good plug-ins should always fit in with the native WordPress admin pages. I’ve used the ‘WordPress’ tabs in on of my plug-ins, however I implemented it slightly differently: the options were all on one page with javascript handling the tab effect. The advantage of this is that users can navigate between tabs without having to save the options for that page each time.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Yep – definitely a viable alternative. The key to this, though, is that settings can be all grouped together in a single section rather than doing multiple sections (like we have above with “Display” and “Social”).

    • James

      How do you do this in jquery? I think the tutorial is outstanding, however it seems annoying with the reload of the page… if you can make it more “smooth” and save a second using jquery for tab-switching, why do it this way? I understand it’s great for educational purposes though, but not very practical?

      Don get me wrong. GREAT job, putting this series toget

  • http://www.paulund.co.uk Paul

    Great series thanks for your help. Better start making those themes.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Get to work! ;)

  • Pingback: Wordpress - API | Pearltrees

  • http://twitter.com/peteweb Pete

    Great series of tutorials – best I’ve come across for the Settings API…! Will any of the additional articles you’re working on touch again on security and input sanitisation?

    • http://tommcfarlin.com Tom McFarlin
      Author

      Thanks!

      And yep – I’m planning to hit security, santization, and input types during the last two articles in the series.

  • http://coder-design.com/ coder-design

    I learn a lot here. Thanks for sharing.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Sure thing! :)

  • NewThemer

    Thank you so much for taking the time to write this tut, it has taught me a lot more than just the settings API. I look forward to reading and learning more from you in the future. Thanks again : )

    • http://tommcfarlin.com Tom McFarlin
      Author

      Glad you found it useful!

  • Pingback: The Complete Guide To The WordPress Settings API, Part 5: Tabbed Navigation For Your Settings Page | Wptuts+

  • Steven

    What if, instead of tabs, each section was repeatable? This might be useful if you were creating different bits of custom code each with custom features to be output using shortcodes.

    Is this possible?

    • http://tommcfarlin.com Tom McFarlin
      Author

      Hey Steven – not sure I understand the question. Can you give a concrete example of what you’re getting at?

      • Steven

        I am referring to having multiple instances of the same field or section by duplicating a field or section as many times as you need it. Does that make sense?

        • http://tommcfarlin.com Tom McFarlin
          Author

          A field corresponds to a single option and each section corresponds to a collection of fields.

          So, for example, say that you have a section for contact information and you have three fields – the user’s email address, Twitter account, and their Google Voice number.

          If I understand your question correctly, you’re asking about the case where you’d want to repeat a section like that.

          The Settings API requires that each field and section have a unique ID so you’d have to manually create them; however, if you’re looking to duplicate the input similar to how I have above (with multiple calls to settings_fields and do_settings_sections, you’ll still have to use some mechanism (like a query string parameter) because WordPress will write out each section with its own nonce value so the last section to be written out to the page will be the only one that has its options serialized.

          A bit complicated, I know. Truth is, I’m not really a fan of automating duplication of things like this.

          • Steven

            That is what I am referring to. I do know that it is complicated. I have noticed that other theme creators have worked around this by not using the Settings API at all. I think it is better to use the settings api because it is a cleaner integration with WordPress.

            Also…why not a fan of automating duplication?

            Thanks!

          • http://tommcfarlin.com Tom McFarlin
            Author

            I should clarify: I’m a fan of automatic duplication – after all, that’s one of the reasons we program, right? I just mean within the context of the Settings API.

            Each field needs to have a unique ID and should belong to a section. To that end, I don’t think you should try to duplicate certain fields – the ID’s can be come unclear, the validation and sanitization would have to be generalized, and overall maintenance overtime would become more complicated.

            There’s definitely a proper time for abstracting aspects of our code, but I’m of the opinion that trying to do so with WordPress Settings is not it.

  • Anthony

    Cool, what about logo uploading in this theme options?

    • http://tommcfarlin.com Tom McFarlin
      Author

      Hey Anthony – I’ll consider it. Right now, I’m planning to only cover the basic element types as well as how to sanitize and validate their data.

      • Anthony

        Thanks for the reply! Tom, I think not only me wish to know how to implement such logo uploading function in theme options. I am googling for this within two days and no result. I found interesting solution here – http://aquagraphite.com/ But I don’t like it too much coz it don’t look like native WordPress look and feel. Maybe I could donate you or something else?

  • Anthony

    Tom, it’s possible to update an article or made part 6 to explain people how to make simple file uploading that’s very useful for custom logo? I am trying to do this feature within two days using this from WP Codex – http://codex.wordpress.org/Function_Reference/wp_handle_upload#Source_File but no result :( Thanks!

    • http://tommcfarlin.com Tom McFarlin
      Author

      See my previous comment :).

  • http://www.johnimbong.com John Imbong

    When displaying an icon for the page, you can also use . I think it's more consistent that way, yah?

    This tut is tops.

  • http://www.johnimbong.com John Imbong

    I understand that there won’t be any other additions to this post on how to upload an image, so for everyone out there, do you have any ideas on how to do it? That would be the cherry on top of this 5-page masterpiece.

  • http://thetibashole.tumblr.com Tiziano

    What a great series and what a great article!!!!

    I didn’t know how to implement tabbed navigation before reading this article, and I’ll need it soon for a new plugin; thanks a lot Tom ;-)

    • http://tommcfarlin.com Tom McFarlin
      Author

      Thanks Tiziano – glad it’s been helpful!

  • http://www.moonworkspublishing.com Joe Jenkins

    A great tutorial

    I’ve pretty much got things sorted, but have one problem I can’t figure out.

    I want the text input to have just the text I add, but I can’t seem to get rid of the http:// appearing within the text box when I save the info.

    • http://tommcfarlin.com Tom McFarlin
      Author

      This is happening because of the esc_url_raw mentioned in this article.

      Remove that function call and you’ll be good to go!

  • Pingback: The Complete Guide To The WordPress Settings API, Part 6: Menu Pages | Wptuts+

  • Pingback: The Complete Guide To The WordPress Settings API, Part 6: Menu Pages | How to Web

  • http://krymsonproductions.com Matthew Caldwell

    Hey, great tutorials.

    I’m having a bit of trouble with getting my social options to save. Before this article I was having trouble with them saving because the display options were the only ones saving. I assumed that’s because I changed the display options last so that nonce was newest. No big deal, but now I’ve followed this page and gotten the tabbed navigation working, looks great, social options still don’t save. It’s driving me up the wall! All of my code looks just like yours, I can’t find anything wrong with it. I even see the social nonce when I am on that tab… any idea why it’s not saving?

    • Tom McFarlin
      Author

      Hard to say without actually seeing it. Be sure to checkout the project an GitHub and make sure you’ve got everything exactly as I have there.

      My guess is that it’s some small nuance you’ve missed. Always the case, right? :)

  • http://www.greenleaf-net-solutions.com/ Colin Crawford

    Thanks Tom, from Part 4 to Part 5 I nearly slipped up when you altered the conditional statement, wasn’t sure if you meant change the first part or the middle bit. Tried both ways and obviously the last one worked.

    Again, need to go through it all again for when I’m building my Theme Options page.

  • WPGuy

    Hi,
    Fantastic tutorial as always :)

    BTW, can we fetch each set of options via ajax ?
    Please give some guidelines on how this can be done…

  • Jo

    Tom, this is a another great piece!!

    Btw, I find it more friendly if i get redirected to a default tab. Here is what i change inside sandbox_theme_display function to get a default tab:

    <?php
    if( isset( $_GET[ 'tab' ] ) ) {
    $active_tab = $_GET[ 'tab' ];
    } // end if
    ?>

    with

    <?php
    if( isset( $_GET[ 'tab' ] ) )
    {
    $active_tab = $_GET[ 'tab' ];
    }else{
    //set display_options tab as a default tab.
    $active_tab = ‘display_options’ ;
    }
    ?&gt

  • Erk

    First of all thank you for this awesome tutorial…

    After I click the menu link none of the tabs are active in the page.It becomes active when I click on a tab.

    Am I missing something here?

    • Erk

      Nevermind I added “$active_tab = ‘display_options’;” infront and it works now.

  • Pingback: Integrating With WordPress' UI: The Basics | Wptuts+

  • Pingback: Integrating With WordPress’ UI: The Basics | Wordpress Webdesigner

  • Pingback: My Stream | Integrating With WordPress’ UI: The Basics | My Stream

  • Pingback: Andre's Web Dev: WP Class Blog » Adding Theme Options in the WordPress Admin Dashboard

  • http://sdavismedia.com/ Sean Davis

    This is awesome. Thanks a ton for writing. I’m building my own framework and articles like this go a long way in making this happen.

  • http://www.caercam.org/ Charlie Merland

    Brilliant paper! One thing though: you might want to consider adding a word in the conclusion precising that reading part 5 is needed to get the whole piece to work; lost some time figuring that out :)

  • Pingback: What’s Next For You Online? What Are You Working On? What Are Your Goals? | SDavis Media

  • Shiba

    This helped me solve a big problem!!! Thank you so much! :)

  • Pingback: Deborah’s Weekly Web Resources Roundup: April 1, 2012

  • Pingback: What’s Next For You Online? What Are You Working On? What Are Your Goals? | sdavismedia