Last time I wrote about using the Theme Customizer to supplant/add to your existing options. But what if you’re writing a new theme entirely?

Do you even need an options page?

How many options does your theme have, anyway? Would it not make more sense to eschew those complex options pages in favor of just allowing the user to do it all “live”? With the Theme Customizer, this becomes entirely possible. As an added bonus, it also gives us a really handy use for the theme_mod system.

Step 1: Surfacing the Customizer

One thing I don’t like about the theme customizer is how hidden it is. It’s in the Themes selection space, under the strangely worded “Live Preview” option. Let’s bring that to the forefront and make it more visible for our example theme. In our functions.php file:

add_action ('admin_menu', 'themedemo_admin');
function themedemo_admin() {
	// add the Customize link to the admin menu
	add_theme_page( 'Customize', 'Customize', 'edit_theme_options', 'customize.php' );

There we go. Now we have a “Customize” link in the Appearance menu that loads up the customizer. Nice.

(Note, using the “customize.php” link works with the currently active theme only. Adding ?theme=themedemo to it would allow links to customize non-active themes, but this makes no real sense in the theme functions.php file of a theme. But if you were doing a plugin that needed to load the customizer, that might be good to know.)

Don’t do this anymore, kids. WordPress brought the Customize link to the surface in a later version.

Step 2: Adding some options

Let’s add some simple options to the theme customizer. How about a text field and, say, a color picker. Why not?

add_action('customize_register', 'themedemo_customize');
function themedemo_customize($wp_customize) {

	$wp_customize->add_section( 'themedemo_demo_settings', array(
		'title'          => 'Demonstration Stuff',
		'priority'       => 35,
	) );

	$wp_customize->add_setting( 'some_setting', array(
		'default'        => 'default_value',
	) );

	$wp_customize->add_control( 'some_setting', array(
		'label'   => 'Text Setting',
		'section' => 'themedemo_demo_settings',
		'type'    => 'text',
	) );

	$wp_customize->add_setting( 'some_other_setting', array(
		'default'        => '#000000',
	) );

	$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'some_other_setting', array(
		'label'   => 'Color Setting',
		'section' => 'themedemo_demo_settings',
		'settings'   => 'some_other_setting',
	) ) );


We now have “some_setting” and “some_other_setting”. You can tell that I’m being very creative with my naming scheme here. 😉

Note that I didn’t define the “type” for these settings. This means they’ll be the default type, which is “theme_mod”, and that’s perfect for this case. Since we’re providing no other way than the customizer to change these settings, then it’s nice and easy to use them.

Speaking of using them…

Step 3: Use the settings in your theme

Here’s our theme’s index.php file:

<?php get_header(); ?>

<h3>Theme Customizer Demo, using theme_mod and <em>no</em> settings page!</h3>


echo 'some_setting => ' .get_theme_mod( 'some_setting', 'default_value' )."\n";
echo 'some_other_setting => ' .get_theme_mod( 'some_other_setting', '#000000' )."\n";
echo 'non_existent_setting => '.get_theme_mod( 'non_existent_setting', 'default_value' )."\n";


<?php get_footer(); ?>

The header and footer are pretty standard. Nothing special. You can find them in the download. What we’re really concerned with here is how the theme uses the settings themselves.

Notice the use of get_theme_mod. It’s pretty darned easy to use, really. It just gets the settings and uses them however the theme needs to. For this demo, I’m just outputting their contents for demonstration purposes. I also added in a non-existent setting to show that it works and outputs the default value when nothing is set for the theme in the theme_mod system.

The nice thing about theme_mod is that it automatically stores the information in a nice packed way, unique to the theme itself. If you have multiple copies of the same theme on the site, but with different names, each will have its own settings. Easy and handy.

Step 4: There is no step 4.

Seriously, that’s it. You can go further and make the settings work “live” using the postMessage methods I explained in the previous post, but for the most part, there’s nothing more to do. This is a simple and easy way of making settings, using them, and not worrying about complex options pages, settings in the database, where they’re stored… Why do all that extra code if you don’t have to?

Here’s a link to a working theme using the above concepts. For fun, it also supports custom background colors. 🙂

This is just one example of how to use the core APIs in a somewhat handy way. There’s other ways too. Feel free to experiment.



  1. I love the fact that I can add my custom options to the customize.php page, it’s much more intuitive for people to use than a custom theme options page. One question… is there any way to allow the WP_Customize_Image_Control function to choose pre-existing images from the media library?


    • This would be a great feature to add. It seems counter-intuitive that any uploaded image(s) via WP_Customize_Image_Control will go into the media library but you can’t select existing media images.

      • @Cassie, @David

        I had to extend WP_Customize_Image_Control class and add my own tab_uploaded() function to filter the attachments.

        If you want to use a context for your uploaded images (e.g. you’re creating a favicon uploader and only need to return already uploaded favicons), seems that all you have to do is to define a ‘context’ when you create a control. If you do this, WP will create a _wp_attachment_context meta key with the value provided inside your add_control()</code. Then you'll be able to use get_posts() the same way it's used in WP_Customize_Background_Image_Control.

        Here's my code:

        Hope that helps. : )

        • @Eduardo,
          Thank You for this code snippet. I see how this adds the uploaded media library to custom image controllers, but for some reason, the only images that show are the background images previously uploaded. Is there something i’m missing to make sure that the previously uploaded images that display are from that specific custom image upload controller?

  2. This is a fantastic tutorial.

    I second Cassie’s question. Also, it’d be great to be able to add instructions to a section other than the settings labels. But even as a limited tool it’s very useful.

  3. It’s awesome! Can’t wait for other fields, like textarea, uploads…

  4. A little advice: do not include the add_action('customize_register', 'whatever_customize'); inside a if ( is_admin() ) condition… It won’t work (yeah, I spent half an hour with this >_<).

    Thanks Otto 🙂

  5. Thanks for the tutorial Otto! Really helpful 🙂

  6. […] HomeWordPress PluginsAfter The Deadline For CommentsAtom Default FeedGravatar BoxPHP Code WidgetSimple Facebook ConnectSimple Twitter ConnectUnicornifyContact OttoNothing to See Here « Theme Customizer Part Deux: Getting rid of Options pages […]

  7. thanks for the demo, really helpful.
    How would I go about making repeatable fields? like images for a slideshow for example


    by the way, the sign in with Twitter is broken

  8. Great tutorial, thanks.

    I am having trouble adding an image option though. For some reason, it works fine when I go to the customization page (I see the default image), but on the frontend I get the following:

    instead of the proper image path.

    These are the snippets of code I am using:


    $wp_customize->add_setting( ‘change_branding’, array( ‘default’ => get_bloginfo(‘template_directory’) . ‘/images/logo.png’, ) );


    <img class="bisbranding" src="” />

  9. Woops, it stripped out the code…

    Basically the image src is “default” when the page is loaded as normal. However, it works fine (points to the right location) when loaded through customization.

    In my header.php page I have PHP: “echo get_theme_mod( ‘change_branding’, ‘default’ )” inserted into the src tag.

    Any ideas?

  10. Many thanks, now I’m starting to get my head around it.
    The theme customizer looks like a great feature and totally agree it should find a better place in the default admin.

    A little OT: I don’t know if it’s just me but the ‘sign in with Twitter’ for commenting does not seem to work properly forwarding to an incorrect address.

  11. This is really helpful for WordPress developers. Thank you Otto for sharing the information.

  12. I have to say this has a lot of promise instead of using theme option plugins, scripts, or even the settings API to create option pages. Is this customizer to replace the settings api ?
    Also, I discovered something…if you code this with the add_control before the add_setting, the customizer window goes blank. Apparently it does not like this sequence.

    IF this customizer is the way to start doing theme settings and options for customizing, I’m definitely in!

  13. This is awesome thanks! One question though how can I put a reset to default button in there? Works great for me though, I would just love to have that there as an option.

  14. Great work Otto. I’ve been looking for a way of getting rid of clunky complicated theme options pages in my themes. You’ve shown me the light 😉

  15. Thanks for this helpful tutorial, Otto.

    I got the Customizer working the way I want for the most part, but one thing seems odd to me: in order for WordPress to register a particular theme mod setting to the database, I need to first make some kind of change on the Customize page and click “Save & Publish”. Querying for a setting using get_theme_mod() before clicking “Save & Publish” returns a null value, unless I specify a default value in the function call.

    What seems weird is that, unless I go to the Customize page and make a nominal change so that all theme mod settings are saved to the database, I effectively then have to specify a default value for a setting twice: once when adding the setting in the $wp_customize->add_setting() call, and once again when querying for the setting using get_theme_mod().

    I’d rather just force WordPress to write all the settings to the database using the same default I set in the $wp_customize->add_setting() call, so that I don’t have to specify a default value for the setting again the the get_theme_mod() call. (I tried forcing WordPress to write the settings to the database by default using the set_theme_mod() function, but I wasn’t able to get it to work.)

    Perhaps I’m missing something? I’d really appreciate any help you could provide. Thanks again for a great tutorial!

    • Correct. So, always specify a default value in the get_theme_mod function call.

      These are two different defaults. One is for the place you retrieve it, and the other is for telling the customizer what the default should be if it’s not in the database. These usually would be the same value, and yes, you’re specifying it twice.

      Writing the defaults to the database is always a bad idea, because then they’re not really “defaults” any more. Down this path leads confusion and weird results that you’re not expecting.

      My advice: Get over it, and specify the default twice. Really. You’ll save yourself a whole lot of hassle in the long run. If you want to define an array of defaults somewhere and then just reference it twice, that’s fine, do that.

  16. Hi Otto,

    I there a way to put a reset to default button in there so it reverts back to the default settings?

  17. […] had a series of articles highlighting how to use the theme customizer in your WordPress themes instead of creating […]

  18. Hi Otto, so after some re-search (though I have not gone to see the core code) what would are the types available for the controls besides ‘type’ => ‘text’,?

    P.S: Also your comments are kinda broken when you log in with twitter 😉

    ERROR: please fill the required fields (name, email).

  19. Hi Otto,

    I’m trying to get the Background Color control, which is in the Colors section by default, to have a priority 1 – as I’m adding color controls for a number of other areas in my theme. I have all the other controls in this section respecting a priority order – but the default background_color is stumping me. I used your add_theme_support in my functions file to set the default color, thank you. Is there something similar I can do in my functions file to set the priority on the color control for this setting?

  20. OK. Great Start. Can you please finish a litte more on the Step 3? It doesn’t really wrap things up enough to be able to implement properly into page elements. Also, not sure why, but when using custom id’s I get a php error on the site. I was hoping for a step 4 to mirror the controls on a custom admin panel as well as how the CSS is cued or generated. I am very interested in relying only on the existing WordPress API’s and js files. I’ve attempted to use Options Framework and OptionTree, but I’m extremely afraid to sell a theme that will have a conflict when updating. It seems that every source I can find online about creating custom wordpress admin panels are incomplete, and no offer little to no support…

  21. I’ve been using the Theme Customizer in some themes and loving it. So I’m wondering if we could also use the Theme Customizer in plugins if they have some styling that needs to be customized? Or if we can, should we?

    The controls and the live preview are incredible, and I could see how I could use them to provide some flexibility to a plugin. But would that make sense to an end user, to have this under the same place where they customize a theme? Or is it better to keep plugins into their own option / setting pages?

  22. Is there a way for the upload image control or some other control to access the media library??

  23. When i add the “Customize” link to the admin panel as described in the intro it also appears under “Options” in the “Themes” panel. So in the end “Customize” appears twice in the themes panel. How can i avoid that or delete one entry?

  24. Just following up my last comment (which hasn’t been approved yet so I can’t just reply)

    I was right in my assumption. I placed the following in functions.php:

    add_theme_support( 'test' );

    I then used it as follows:

    $wp_customize->add_section( 'test_a_section', array(
    'title' => 'Test Section',
    'priority' => 35,
    'theme_supports' => 'test',
    ) );

    In this case, commenting out get_theme_supports() has the expected behavior. Since I like to provide a lot of functionality that requires configuration and styling when added by a child theme (conditionally adding a lot of JS and CSS stuff is much better than just adding unused junk to a theme), this is an AWESOME feature to have at my command.

  25. Great post ,thank you very much!

  26. […] go back to the theme demo I used in the second customizer tutorial post, and add a new setting and control to demonstrate […]

  27. Otto, you have a gift for explaining this type of stuff. Very easy to follow. Thank you!

  28. […] “Otto” Wood on the customizer: Parts One (basic utilization), Two (dumping options pages), and Three (custom […]

  29. Otto, thank you for a great tutorial. I have a question, though: is it possible to have 2 different customizers in the same theme? I mean, I want to customize the main page AND an inner page, and no matter what I try, I can’t do it. I mean in Live mode, it works like a charm if I reload, just can’t make it work in live mode, even if I navigate to the page I want to edit

  30. Thanks a lot!!!! I have been searching how to do that for a few days and I couldn’t find anything. In WordPress Codex there is not enough documentation about it.

  31. […] back in part two, I had a bit about Surfacing the Customizer. That bit is outdated now, WordPress does this for you […]

Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Need to post PHP code? Wrap it in [php] and [/php] tags.