Skip to main content

Course Progress

Loading...

🎨 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
The Customizer API follows a hierarchy: Panels contain Sections, Sections contain Settings, and Settings use Controls for user input.

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_Control

Textarea 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_Control

Image Control

Media library selector

WP_Customize_Image_Control

Media Control

Any media file selector

WP_Customize_Media_Control

Cropped Image

Image with crop interface

WP_Customize_Cropped_Image_Control

Text-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

Create a comprehensive customizer implementation that includes:

  1. Create a "Theme Options" panel
  2. Add sections for Header, Colors, Typography, and Layout
  3. Implement color controls for primary and secondary colors
  4. Add typography controls (font family, size, line height)
  5. Create layout options (sidebar position, container width)
  6. Implement live preview for all color settings
  7. Use selective refresh for text content
  8. Create a custom range slider control
  9. Add proper sanitization for all settings
  10. Style the output based on customizer values

Additional Resources