Separate multiple Theme Options Pages using Tabs

Recently it became a requirement that themes submitted to the WordPress Directory have its options pages residing under the Appearance menu. This is intended to create a more consistent user interface and to standardize the locations where theme components can be found in the admin backend. Now all the theme’s features can be in the same place …

Themefuse Recently it became a requirement that themes submitted to the WordPress Directory have its options pages residing under the Appearance menu. This is intended to create a more consistent user interface and to standardize the locations where theme components can be found in the admin backend. Now all the theme’s features can be in the same place: If your theme supports custom headers or backgrounds, these can be near your theme options page. It also avoids clashing menu pages, which can happen if a top-level menu entry is chosen.

This shouldn’t be a problem if the theme has only one options page (and this should be the case for most themes) or even 2 pages, but what if the theme options split into 4, 5 or 6 pages? Themes offering advanced functionality like frameworks may require such an interface if it’s offering a lot of options that cover more than one area. In this case, placing many submenu entries under the Appearance menu would be an ugly option, as these would get lost among the other option pages and would create a unnecessarily long and confusing submenu.

The solution in this case is to create one single menu entry for the theme options and use tabs to separate the options pages. Each page will still be stand alone and have its own URI so it can be managed just like any other options page.

In order to create the tabs, we create a function that generates them from an array and outputs the links for them:

function mytheme_admin_tabs( $current = 'general' ) {
	$tabs = array( 'general' => 'General', 'layout' => 'Layout', 'advanced' => 'Advanced' );
	$links = array();
	foreach( $tabs as $tab => $name ) :
		if ( $tab == $current ) :
			$links[] = "<a class='nav-tab nav-tab-active' href='?page=mytheme_options&tab=$tab'>$name</a>";
		else :
			$links[] = "<a class='nav-tab' href='?page=mytheme_options&tab=$tab'>$name</a>";
		endif;
	endforeach;
	echo '<h2>';
	foreach ( $links as $link )
		echo $link;
	echo '</h2>';
}

Now let’s see what we have done:

  • We are storing the tabs (in the form slug => name) in an array called $tabs
  • We are creating the links in an array called $links
  • We are outputting the tabs wrapped in a h2 tag

Note: Replace mytheme_options with your current theme options page’s slug.

The tabs will now show at the top of the options page with the proper links. As you can see, we have added an argument &tab=slug to the page’s URI so we can check which tab is currently viewed. This argument is ignored by the WordPress core and its presence will not affect its functionality in any way.

All we need to know now is the tab we are viewing so we know which page to output. We do this with a simple call to $_GET['tab'] and call the function that generated the options page:

if ( $pagenow == 'themes.php' && $_GET['page'] == 'mytheme_options' ) :
	if ( isset ( $_GET['tab'] ) ) :
		$tab = $_GET['tab'];
	else:
		$tab = 'general';
	endif;
	switch ( $tab ) :
		case 'general' :
			mytheme_general_options();
			break;
		case 'layout' :
			mytheme_layout_options();
			break;
		case 'advanced' :
			mytheme_advanced_options();
			break;
	endswitch;
endif;

Here’s what we did:

  • First we checked if the current page is our theme’s options page, in order not to conflict with any other pages that may use tabs.
  • We checked what tab is set in the URI and defaulted it to ‘general’ because WordPress doesn’t add a tab to the menu link, it does not know about it.
  • We called the required function to generate the options tab, depending on the current tab.

That’s it! Our options page is now separated in tabs. Sorry to disappoint you if you expected it to be more complicated. Forget about jQuery and AJAX content generators that just bloat your code and reduce the user experience. This is a streamline method, simple and clean that lets you control a theme with lots of options.

Hope to see more clean and useful options pages in themes from now on.

14 thoughts on “Separate multiple Theme Options Pages using Tabs

  1. Hi,
    Would you have a simple example of where the code goes, I understand the mytheme_general_options(); loads a page.

    But I cannot see where the call is to mytheme_admin_tabs(), or where the $pagenow code goes, I tried in in and out of a page function but got a undifined variable.

    If we had a simple example with just a header and one field saved to an option would help.

    I really want to use it in a free plugin I am creating as there are a few pages and this would look so nice.

    Here is the plugin info, I will credit all sources when the plugin is released.

    http://digitalraindrops.net/facebook-tutorial/

    David

    1. The mytheme_admin_tabs() function outputs the tabbed links, so you should put it where you want the tabs to show up.

      The $pagenow variable is defined by WordPress. If you’re using it inside a function, you must set is as global and you can drop the call to it if using it inside the theme options function because it’s that page anyway.

      The call to the code should be in the function that generates the theme’s options page, i.e. the callback in add_theme_page

  2. Hi Daniel,
    Thanks for the prompt reply, I understand it in theory, I had not thought of the global, I like most prefer to look at an example, rather than bits of code, as it giver the whole picture.

    I will have a play if I can find some time, but it will have to go on the back burner for now!

    Thanks

    David

  3. I’m trying to use this script with four tabs and when I switch tabs, the first tab always stays highlighted. I believe it has something to do with $current being defined as the first tab name, but no matter how I rearrange the code, it either stays as was or none of the tabs are highlighted as the current.

    Any help you can offer would be appreciated.

    1. +1 to providing the code… I have 4 tabs as well and cant get the $current to change from anything but ‘general’

  4. If you add class=”nav-tab-wrapper” to the h2 tag around the links you’ll get that nice line at the bottom of the tabs.

    Thanks for the great post! Saved me a ton of time.

  5. Great post thanks!

    How do I add the options panel to show in menu instead of theme page?

    I’m tryin to use this add_menu_page but it only shows blank page inside “Theme Options”. By changing add_theme_page to add_menu_page did work ok in the without tabs version.

    1. I was able to identify the problem. Just need to change the $pagenow == ‘themes.php’ to $pagenow == ‘admin.php’

  6. Hi – thanks for this great advice.
    I had to consult David Cox’s post (see above) to understand the implementation completely. So I thought I would share it here as he also solves Serlorian’s query about making the active tab display properly:

    // Function to generate options page
    function mytheme_home_page() {
    global $pagenow;
    if ( $pagenow == 'themes.php' && $_GET['page'] == 'mytheme_options' ) :
    if ( isset ( $_GET['tab'] ) ) :
    $tab = $_GET['tab'];
    else:
    $tab = 'general';
    endif;
    mytheme_admin_tabs ($tab); // this is where to call the function which makes the tabs and also pass $tab as the current tab...
    switch ( $tab ) :
    case 'general' :
    mytheme_general_options();
    break;
    case 'layout' :
    mytheme_layout_options();
    break;
    case 'advanced' :
    mytheme_advanced_options();
    break;
    endswitch;
    endif;
    }

    I would recommend downloading David Cox’s sample theme.
    Also if this is beyond your level, time or resources I strongly recommend Devin’s Options Framework http://wptheming.com/options-framework-theme/ – a phenomenal piece of coding!
    Once again thanks to all ๐Ÿ˜€
    Mike

Leave a Reply

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