WordPress Customizer API: A Complete Developer Guide

The WordPress Customizer API gives theme and plugin developers a standardized way to add live-preview options to the WordPress admin. Instead of custom settings pages with a save-and-reload workflow, the Customizer lets users see changes in real time before publishing.

This guide walks through the full API: architecture, adding panels/sections/settings/controls, selective refresh, postMessage transport, sanitization, and best practices for 2026.

Key Takeaway:

Customizer API Architecture

The Customizer is built on four hierarchical objects:

  • Panels — top-level groupings (e.g., “Theme Options”)
  • Sections — groups within a panel (e.g., “Header”, “Colors”)
  • Settings — individual stored values (saved to wp_options or theme_mods)
  • Controls — the UI elements bound to settings (text, color picker, select, etc.)

All Customizer registration happens in a callback hooked to customize_register, which receives a WP_Customize_Manager instance.

Registering a Panel, Section, Setting, and Control

add_action( 'customize_register', function( WP_Customize_Manager $wp_customize ) {

    // Panel
    $wp_customize->add_panel( 'theme_options', [
        'title'    => __( 'Theme Options', 'my-theme' ),
        'priority' => 160,
    ] );

    // Section
    $wp_customize->add_section( 'header_options', [
        'title'    => __( 'Header', 'my-theme' ),
        'panel'    => 'theme_options',
        'priority' => 10,
    ] );

    // Setting
    $wp_customize->add_setting( 'header_bg_color', [
        'default'           => '#ffffff',
        'transport'         => 'postMessage',
        'sanitize_callback' => 'sanitize_hex_color',
    ] );

    // Control
    $wp_customize->add_control(
        new WP_Customize_Color_Control(
            $wp_customize,
            'header_bg_color',
            [
                'label'   => __( 'Header Background Color', 'my-theme' ),
                'section' => 'header_options',
            ]
        )
    );
} );

Retrieving Customizer Values in Templates

Use get_theme_mod() for theme_mods storage (recommended for theme options) or get_option() for options storage:

// In your theme template or functions.php
$header_bg = get_theme_mod( 'header_bg_color', '#ffffff' );

// Inline CSS output
add_action( 'wp_head', function() {
    $header_bg = get_theme_mod( 'header_bg_color', '#ffffff' );
    $header_bg = sanitize_hex_color( $header_bg );
    echo '<style>
        .site-header { background-color: ' . esc_attr( $header_bg ) . '; }
    </style>';
} );

postMessage Transport with JavaScript Bindings

The postMessage transport avoids iframe refreshes by communicating directly with the preview pane via JavaScript. You must enqueue a JS file and write bindings:

// Enqueue customizer JS
add_action( 'customize_preview_init', function() {
    wp_enqueue_script(
        'my-theme-customizer',
        get_template_directory_uri() . '/js/customizer.js',
        [ 'customize-preview' ],
        '1.0.0',
        true
    );
} );
// js/customizer.js
( function( $ ) {
    wp.customize( 'header_bg_color', function( value ) {
        value.bind( function( newval ) {
            $( '.site-header' ).css( 'background-color', newval );
        } );
    } );
} )( jQuery );

Selective Refresh for PHP-Rendered Partials

For content that requires PHP re-rendering (e.g., the site title, nav menus, widget areas), use selective refresh instead of full page reload. Change the setting transport to postMessage and register a partial:

add_action( 'customize_register', function( WP_Customize_Manager $wp_customize ) {

    // Setting with refresh transport (default)
    $wp_customize->add_setting( 'site_tagline_display', [
        'default'           => true,
        'transport'         => 'refresh',
        'sanitize_callback' => 'rest_sanitize_boolean',
    ] );

    // Selective refresh partial
    $wp_customize->selective_refresh->add_partial( 'site_tagline_display', [
        'selector'        => '.site-description',
        'render_callback' => function() {
            bloginfo( 'description' );
        },
    ] );
} );

Available Control Types

Core Controls

  • WP_Customize_Control — text, textarea, checkbox, radio, select
  • WP_Customize_Color_Control — color picker
  • WP_Customize_Media_Control — media library uploader
  • WP_Customize_Image_Control — image-only uploader
  • WP_Customize_Cropped_Image_Control — image with crop UI
  • WP_Customize_Date_Time_Control — date/time picker (WP 4.9+)

Custom Controls

Extend WP_Customize_Control to build custom control types. Override the render_content() method:

class My_Toggle_Control extends WP_Customize_Control {
    public $type = 'toggle';

    public function render_content() {
        ?>
        <label>
            <span class="customize-control-title">
                <?php echo esc_html( $this->label ); ?>
            </span>
            <input type="checkbox"
                   value="1"
                   <?php $this->link(); ?>
                   <?php checked( $this->value() ); ?> />
        </label>
        <?php
    }
}

Sanitization Best Practices

Every setting must have a sanitize_callback. Never store raw user input:

// For select/radio — validate against allowed values
function my_sanitize_layout( $value ) {
    $allowed = [ 'boxed', 'full-width', 'sidebar-left', 'sidebar-right' ];
    return in_array( $value, $allowed, true ) ? $value : 'boxed';
}

// For integer ranges
function my_sanitize_range( $value, $setting ) {
    $min  = $setting->manager->get_control( $setting->id )->input_attrs['min'];
    $max  = $setting->manager->get_control( $setting->id )->input_attrs['max'];
    $step = $setting->manager->get_control( $setting->id )->input_attrs['step'];
    return $min <= $value && $value <= $max ? absint( $value ) : absint( $setting->default );
}

Customizer vs. Block Theme Settings (2026)

With WordPress 6.x, block themes (FSE) use theme.json for design tokens and Global Styles UI for live editing. The Customizer is still fully supported for classic themes and hybrid themes but is not the recommended path for new block themes.

Use the Customizer when:

  • Building or extending a classic theme
  • You need conditional logic between settings (show section B only if setting A is enabled)
  • You’re adding non-design functionality (e.g., social media links, contact info, scripts)
  • You need to support WordPress installations where FSE isn’t enabled

Frequently Asked Questions

The WordPress Customizer API (Theme Customization API) is a framework for adding live-preview theme options. It exposes PHP classes for Panels, Sections, Settings, and Controls that render in the wp-admin Customizer UI.
Register everything on the customize_register hook, which passes the $wp_customize manager object. Call $wp_customize->add_panel() for a top-level group, ->add_section() for a section inside it, ->add_setting() to declare the stored value (with default, transport, and sanitize_callback), and ->add_control() to bind a UI control to that setting. Settings hold the data; controls are only their UI — every control needs a matching setting.
Use get_theme_mod( ‘setting_id’, $default ) when the setting is type ‘theme_mod’ (the default), or get_option( ‘setting_id’ ) when you registered it as type ‘option’. Always pass a sensible default as the second argument so the template still renders correctly before the user has saved anything.
The ‘refresh’ transport reloads the entire preview iframe when a setting changes. The ‘postMessage’ transport uses JavaScript to apply changes instantly without a page reload — much faster UX but requires writing JS to handle the update.
Selective refresh sits between full refresh and postMessage: instead of writing JS to repaint an element or reloading the whole preview, you register a partial tied to a setting and WordPress re-renders just that PHP-rendered fragment over Ajax. Use it for content that’s awkward to rebuild in JavaScript — widget areas, nav menus, or any markup produced by a PHP template function.
Use the ‘sanitize_callback’ argument on each setting. Common callbacks: sanitize_text_field() for text, absint() for integers, sanitize_hex_color() for color pickers, and a custom callback for select/radio that validates against allowed values.
No. As of 2026 the Customizer API is not deprecated and still ships in core — classic themes, child themes, and many plugins rely on it. What changed is emphasis: block (FSE) themes steer users toward theme.json and Global Styles, so for a brand-new block theme that’s the path. For classic themes, or options needing conditional logic the block tools don’t cover, the Customizer API remains the correct, supported choice.
For block themes (FSE), prefer theme.json settings and Global Styles — they’re the WordPress 6.x standard. For classic themes or when you need complex conditional logic and dependencies, the Customizer API remains appropriate.

Use the postMessage transport for the setting and bind a small JavaScript handler in the Customizer preview that updates the DOM as the value changes. This gives instant visual feedback without a full reload. For dynamic PHP-rendered output, pair postMessage with selective refresh so only the affected partial re-renders.

Use the active_callback argument when registering a control to show or hide it based on the value of another setting. The callback returns true or false depending on the related control’s current value, so the Customizer reveals only the options relevant to the user’s current choices.

Yes. Extend the WP_Customize_Control class to build custom controls – for example a repeater, a custom color or media picker, or a specialized input – then register your control and enqueue its scripts and styles. This lets you offer editing UI the default control types do not provide.

Map each Customizer option to its theme.json equivalent – colors, typography, spacing, and layout move into settings and styles, while global styles handle site-wide design. Some dynamic options may become block styles or patterns instead. Plan a migration that preserves users’ existing choices where possible, since the models differ.

Related Resources

More WordPress development services and guides for theme customization.

Custom Gutenberg Block Development

Build reusable blocks with register_block_type and block.json.

Read article

Custom WordPress Theme Development

Full-service theme development from Figma to production.

View service

WordPress Theme Customization Guide

Options, child themes, and custom modifications explained.

Read article