🎨 Theme Customizer API
Add live preview settings and controls to your theme
Empower users to customize their site without coding
Learning Objectives
- Understand the Theme Customizer architecture
- Add panels, sections, and settings
- Create various control types
- Implement live preview with JavaScript
- Use selective refresh for better performance
- Sanitize and validate customizer input
- Create custom controls
- Organize customizer options effectively
Understanding the Theme Customizer
The Theme Customizer provides a user-friendly interface for making theme changes with live preview. It's the recommended way to implement theme options in WordPress.
Key Concept
Customizer Hierarchy
Panel
→
Groups related sections (optional)
Section
→
Contains related settings
Setting
→
Stores the value in database
Control
→
UI element for user input
Basic Customizer Implementation
Registering Customizer Settings
<?php
/**
* Add Customizer settings
*/
function mytheme_customize_register( $wp_customize ) {
// Add a Panel (optional - for grouping sections)
$wp_customize->add_panel( 'mytheme_panel', array(
'title' => __( 'Theme Options', 'mytheme' ),
'description' => __( 'Customize your theme settings', 'mytheme' ),
'priority' => 10,
) );
// Add a Section
$wp_customize->add_section( 'mytheme_colors', array(
'title' => __( 'Colors', 'mytheme' ),
'panel' => 'mytheme_panel', // Assign to panel
'priority' => 10,
) );
// Add a Setting
$wp_customize->add_setting( 'mytheme_accent_color', array(
'default' => '#667eea',
'sanitize_callback' => 'sanitize_hex_color',
'transport' => 'refresh', // or 'postMessage' for live preview
) );
// Add a Control
$wp_customize->add_control( new WP_Customize_Color_Control(
$wp_customize,
'mytheme_accent_color',
array(
'label' => __( 'Accent Color', 'mytheme' ),
'section' => 'mytheme_colors',
'settings' => 'mytheme_accent_color',
)
) );
}
add_action( 'customize_register', 'mytheme_customize_register' );
Using Customizer Values in Your Theme
<?php
// Get customizer value with default fallback
$accent_color = get_theme_mod( 'mytheme_accent_color', '#667eea' );
?>
<style>
.site-header {
background-color: <?php echo esc_attr( $accent_color ); ?>;
}
a {
color: <?php echo esc_attr( $accent_color ); ?>;
}
.button-primary {
background-color: <?php echo esc_attr( $accent_color ); ?>;
}
</style>
Available Control Types
Text Control
Basic text input field
WP_Customize_ControlTextarea Control
Multi-line text input
type: 'textarea'Checkbox Control
Boolean on/off option
type: 'checkbox'Radio Control
Single choice from options
type: 'radio'Select Control
Dropdown selection
type: 'select'Color Control
Color picker interface
WP_Customize_Color_ControlImage Control
Media library selector
WP_Customize_Image_ControlMedia Control
Any media file selector
WP_Customize_Media_ControlCropped Image
Image with crop interface
WP_Customize_Cropped_Image_ControlText-Based Controls
<?php
// Text Input
$wp_customize->add_setting( 'mytheme_footer_text', array(
'default' => 'Copyright © 2024',
'sanitize_callback' => 'sanitize_text_field',
) );
$wp_customize->add_control( 'mytheme_footer_text', array(
'label' => __( 'Footer Text', 'mytheme' ),
'section' => 'mytheme_footer',
'type' => 'text',
) );
// Textarea
$wp_customize->add_setting( 'mytheme_footer_scripts', array(
'default' => '',
'sanitize_callback' => 'wp_kses_post',
) );
$wp_customize->add_control( 'mytheme_footer_scripts', array(
'label' => __( 'Footer Scripts', 'mytheme' ),
'section' => 'mytheme_footer',
'type' => 'textarea',
) );
// Email Input
$wp_customize->add_control( 'mytheme_contact_email', array(
'label' => __( 'Contact Email', 'mytheme' ),
'section' => 'mytheme_contact',
'type' => 'email',
) );
// URL Input
$wp_customize->add_control( 'mytheme_facebook_url', array(
'label' => __( 'Facebook URL', 'mytheme' ),
'section' => 'mytheme_social',
'type' => 'url',
) );
Selection Controls
<?php
// Checkbox
$wp_customize->add_setting( 'mytheme_show_search', array(
'default' => true,
'sanitize_callback' => 'mytheme_sanitize_checkbox',
) );
$wp_customize->add_control( 'mytheme_show_search', array(
'label' => __( 'Show Search Box', 'mytheme' ),
'section' => 'mytheme_header',
'type' => 'checkbox',
) );
// Radio Buttons
$wp_customize->add_setting( 'mytheme_sidebar_position', array(
'default' => 'right',
'sanitize_callback' => 'mytheme_sanitize_select',
) );
$wp_customize->add_control( 'mytheme_sidebar_position', array(
'label' => __( 'Sidebar Position', 'mytheme' ),
'section' => 'mytheme_layout',
'type' => 'radio',
'choices' => array(
'left' => __( 'Left', 'mytheme' ),
'right' => __( 'Right', 'mytheme' ),
'none' => __( 'No Sidebar', 'mytheme' ),
),
) );
// Select Dropdown
$wp_customize->add_setting( 'mytheme_font_family', array(
'default' => 'sans-serif',
'sanitize_callback' => 'mytheme_sanitize_select',
) );
$wp_customize->add_control( 'mytheme_font_family', array(
'label' => __( 'Font Family', 'mytheme' ),
'section' => 'mytheme_typography',
'type' => 'select',
'choices' => array(
'sans-serif' => __( 'Sans Serif', 'mytheme' ),
'serif' => __( 'Serif', 'mytheme' ),
'monospace' => __( 'Monospace', 'mytheme' ),
'cursive' => __( 'Cursive', 'mytheme' ),
),
) );
Media Controls
<?php
// Image Control
$wp_customize->add_setting( 'mytheme_hero_image', array(
'default' => '',
'sanitize_callback' => 'absint',
) );
$wp_customize->add_control( new WP_Customize_Image_Control(
$wp_customize,
'mytheme_hero_image',
array(
'label' => __( 'Hero Image', 'mytheme' ),
'section' => 'mytheme_hero',
'settings' => 'mytheme_hero_image',
)
) );
// Cropped Image Control
$wp_customize->add_control( new WP_Customize_Cropped_Image_Control(
$wp_customize,
'mytheme_header_image',
array(
'label' => __( 'Header Image', 'mytheme' ),
'section' => 'mytheme_header',
'flex_width' => true,
'flex_height' => false,
'width' => 1920,
'height' => 400,
)
) );
// Media Control (Audio/Video)
$wp_customize->add_control( new WP_Customize_Media_Control(
$wp_customize,
'mytheme_background_video',
array(
'label' => __( 'Background Video', 'mytheme' ),
'section' => 'mytheme_hero',
'mime_type' => 'video',
)
) );
Implementing Live Preview
Use the 'postMessage' transport method and JavaScript for instant preview updates.
PHP: Set Transport to postMessage
<?php
// Enable live preview
$wp_customize->add_setting( 'mytheme_header_bg', array(
'default' => '#ffffff',
'sanitize_callback' => 'sanitize_hex_color',
'transport' => 'postMessage', // Enable live preview
) );
$wp_customize->add_control( new WP_Customize_Color_Control(
$wp_customize,
'mytheme_header_bg',
array(
'label' => __( 'Header Background', 'mytheme' ),
'section' => 'mytheme_header',
)
) );
// Selective Refresh for better performance
$wp_customize->selective_refresh->add_partial( 'mytheme_footer_text', array(
'selector' => '.site-footer .footer-text',
'render_callback' => function() {
echo get_theme_mod( 'mytheme_footer_text', 'Copyright © 2024' );
},
) );
JavaScript: customizer.js
/**
* Theme Customizer Live Preview
*/
( function( $ ) {
// Header Background Color - Live Preview
wp.customize( 'mytheme_header_bg', function( value ) {
value.bind( function( newval ) {
$('.site-header').css('background-color', newval );
} );
} );
// Text Color - Live Preview
wp.customize( 'mytheme_text_color', function( value ) {
value.bind( function( newval ) {
$('body').css('color', newval );
} );
} );
// Site Title - Live Preview
wp.customize( 'blogname', function( value ) {
value.bind( function( newval ) {
$('.site-title a').text( newval );
} );
} );
// Tagline - Live Preview
wp.customize( 'blogdescription', function( value ) {
value.bind( function( newval ) {
$('.site-description').text( newval );
} );
} );
// Font Size - Live Preview
wp.customize( 'mytheme_font_size', function( value ) {
value.bind( function( newval ) {
$('body').css('font-size', newval + 'px' );
} );
} );
} )( jQuery );
Enqueue Customizer JavaScript
<?php
/**
* Enqueue customizer live preview script
*/
function mytheme_customize_preview_js() {
wp_enqueue_script(
'mytheme-customizer',
get_template_directory_uri() . '/assets/js/customizer.js',
array( 'customize-preview' ),
'1.0.0',
true
);
}
add_action( 'customize_preview_init', 'mytheme_customize_preview_js' );
Transport Methods
refresh
Full page reload (default)
Use for: Major layout changes
postMessage
JavaScript live update
Use for: Colors, text, sizes
selective_refresh
Partial refresh via AJAX
Use for: Dynamic content areas
Sanitization Callbacks
Common Sanitization Functions
<?php
/**
* Checkbox sanitization
*/
function mytheme_sanitize_checkbox( $input ) {
return ( ( isset( $input ) && true == $input ) ? true : false );
}
/**
* Select/Radio sanitization
*/
function mytheme_sanitize_select( $input, $setting ) {
$input = sanitize_key( $input );
$choices = $setting->manager->get_control( $setting->id )->choices;
return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
}
/**
* Number sanitization
*/
function mytheme_sanitize_number( $input ) {
return absint( $input );
}
/**
* Email sanitization
*/
function mytheme_sanitize_email( $email ) {
return ( is_email( $email ) ? $email : '' );
}
/**
* URL sanitization
*/
function mytheme_sanitize_url( $url ) {
return esc_url_raw( $url );
}
/**
* HTML sanitization
*/
function mytheme_sanitize_html( $html ) {
return wp_kses_post( $html );
}
/**
* CSS sanitization
*/
function mytheme_sanitize_css( $css ) {
return wp_strip_all_tags( $css );
}
// Built-in sanitization callbacks you can use:
// - sanitize_text_field()
// - sanitize_email()
// - sanitize_hex_color()
// - sanitize_hex_color_no_hash()
// - absint()
// - esc_url_raw()
// - wp_kses_post()
Creating Custom Controls
Custom Range Slider Control
<?php
/**
* Custom Range Control
*/
class Mytheme_Range_Control extends WP_Customize_Control {
public $type = 'mytheme-range';
public function render_content() {
?>
<label>
<?php if ( ! empty( $this->label ) ) : ?>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<?php endif; ?>
<?php if ( ! empty( $this->description ) ) : ?>
<span class="description customize-control-description">
<?php echo esc_html( $this->description ); ?>
</span>
<?php endif; ?>
<div class="range-slider">
<input type="range"
id="<?php echo esc_attr( $this->id ); ?>"
name="<?php echo esc_attr( $this->id ); ?>"
value="<?php echo esc_attr( $this->value() ); ?>"
<?php $this->link(); ?>
min="<?php echo esc_attr( $this->input_attrs['min'] ); ?>"
max="<?php echo esc_attr( $this->input_attrs['max'] ); ?>"
step="<?php echo esc_attr( $this->input_attrs['step'] ); ?>"
/>
<span class="range-value"><?php echo esc_attr( $this->value() ); ?></span>
</div>
</label>
<script>
jQuery(document).ready(function($) {
$('#<?php echo esc_attr( $this->id ); ?>').on('input', function() {
$(this).next('.range-value').text($(this).val());
});
});
</script>
<?php
}
}
// Usage
$wp_customize->add_control( new Mytheme_Range_Control(
$wp_customize,
'mytheme_font_size',
array(
'label' => __( 'Font Size', 'mytheme' ),
'section' => 'mytheme_typography',
'input_attrs' => array(
'min' => 12,
'max' => 24,
'step' => 1,
),
)
) );
Customizer API Methods
| Method | Description | Parameters |
|---|---|---|
add_panel() |
Add a new panel | $id, $args |
add_section() |
Add a new section | $id, $args |
add_setting() |
Add a new setting | $id, $args |
add_control() |
Add a new control | $id, $args or Control object |
get_panel() |
Get a panel object | $id |
get_section() |
Get a section object | $id |
get_setting() |
Get a setting object | $id |
get_control() |
Get a control object | $id |
remove_panel() |
Remove a panel | $id |
remove_section() |
Remove a section | $id |
Customizer Best Practices
Development Best Practices
- Group related settings: Use panels and sections logically
- Always sanitize: Never trust user input
- Provide defaults: Every setting should have a sensible default
- Use live preview: Implement postMessage when possible
- Add descriptions: Help users understand what settings do
- Test thoroughly: Check all control types work correctly
- Consider performance: Don't add too many settings
- Make it translatable: Use proper text domains
Always escape output when displaying customizer values in your theme. Use appropriate escaping functions like esc_attr(), esc_html(), or esc_url().
Practice Exercise
Build Complete Customizer Setup