2026年11月12日
2026年11月12日
WordPress Customizer API: Adding Settings with Live Preview
This article is a localized edition of the Japanese original. Read the Japanese original
The Customizer (Appearance → Customize) lets you change theme settings while watching a preview on the right side of the screen. Themes can add their own settings here with the customize_register hook.
The code in this article was syntax-checked with WordPress 6.9 and PHP 8.3 as of November 2026.
What to know before you start
- It is generally not shown with block themes. When a block theme such as Twenty Twenty-Five is active, "Customize" does not appear in the "Appearance" menu. However, it does appear if a theme or plugin uses the
customize_registerhook. Color and typography settings for block themes are normally handled in the Site Editor andtheme.json. - Settings are saved per theme. The values read with
get_theme_mod(), described later, are saved per theme, so they are not carried over when you switch to another theme.
How the four parts fit together
| Part | Method that adds it | Role |
|---|---|---|
| Panel | add_panel() |
A container that groups several sections (optional) |
| Section | add_section() |
One group shown on the screen |
| Setting | add_setting() |
The saved value. Defines the default, the sanitizing function and the preview method |
| Control | add_control() |
The input for a setting (text field, color picker and so on) |
A section with no controls, and a panel that contains only such sections, are not shown on the screen. If something you added does not appear, first check the section of its controls.
Adding settings
This example goes in functions.php. Put everything in one customize_register callback (defining a function with the same name twice causes a PHP error and a blank white screen).
<?php
add_action( 'customize_register', 'mytheme_customize_register' );
function mytheme_customize_register( WP_Customize_Manager $wp_customize ) {
// Panel
$wp_customize->add_panel( 'mytheme_panel', array(
'title' => 'Theme Settings',
'priority' => 130,
) );
// Sections
$wp_customize->add_section( 'mytheme_colors', array(
'title' => 'Colors and Logo',
'panel' => 'mytheme_panel',
) );
$wp_customize->add_section( 'mytheme_layout', array(
'title' => 'Layout and Footer',
'panel' => 'mytheme_panel',
) );
// Color: color picker
$wp_customize->add_setting( 'mytheme_primary_color', array(
'default' => '#2271b1',
'sanitize_callback' => 'sanitize_hex_color',
'transport' => 'postMessage', // Update the preview with JavaScript
) );
$wp_customize->add_control( new WP_Customize_Color_Control(
$wp_customize,
'mytheme_primary_color',
array(
'label' => 'Primary color',
'section' => 'mytheme_colors',
)
) );
// Image: choose from the Media Library (the attachment ID is saved)
$wp_customize->add_setting( 'mytheme_banner', array(
'default' => 0,
'sanitize_callback' => 'absint',
) );
$wp_customize->add_control( new WP_Customize_Media_Control(
$wp_customize,
'mytheme_banner',
array(
'label' => 'Banner image',
'section' => 'mytheme_colors',
'mime_type' => 'image',
)
) );
// Choices: select box
$wp_customize->add_setting( 'mytheme_layout', array(
'default' => 'sidebar-right',
'sanitize_callback' => 'mytheme_sanitize_select',
) );
$wp_customize->add_control( 'mytheme_layout', array(
'label' => 'Sidebar position',
'section' => 'mytheme_layout',
'type' => 'select',
'choices' => array(
'sidebar-right' => 'Right',
'sidebar-left' => 'Left',
'no-sidebar' => 'None',
),
) );
// Text: footer text
$wp_customize->add_setting( 'mytheme_footer_text', array(
'default' => 'My Site',
'sanitize_callback' => 'sanitize_text_field',
'transport' => 'postMessage',
) );
$wp_customize->add_control( 'mytheme_footer_text', array(
'label' => 'Footer text',
'section' => 'mytheme_layout',
'type' => 'text',
) );
// Preview the footer text with "selective refresh" (see below)
$wp_customize->selective_refresh->add_partial( 'mytheme_footer_text', array(
'selector' => '.site-footer .footer-text',
'render_callback' => function () {
echo esc_html( get_theme_mod( 'mytheme_footer_text', 'My Site' ) );
},
) );
}
// If a value not in the choices is sent, fall back to the default
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;
}
sanitize_callbackis the function that sanitizes a value before it is saved. Specify one for each setting.- The select box sanitizer only lets through values contained in the control's
choices. In this example the setting and the control share the same ID, soget_control( $setting->id )retrieves it. - Besides
text,typeacceptstextarea,checkbox,radio,select,dropdown-pages,email,url,numberand others.
Choosing how the preview updates
A setting's transport decides how the preview is updated when the value changes.
| Method | Setting | Behavior | Good for |
|---|---|---|---|
| Refresh | 'transport' => 'refresh' (default) |
Reloads the whole preview | Layout changes and the like |
| Selective refresh | 'transport' => 'postMessage' + add_partial() |
Re-renders only the specified element in PHP and swaps it in | Text or part of the HTML |
| JavaScript | 'transport' => 'postMessage' + your own JavaScript |
Changes the look with JavaScript without reloading the page | Colors and CSS values |
If you set postMessage and provide neither selective refresh nor JavaScript, the preview does not change (the front end still reflects the value once saved).
Selective refresh
The add_partial() in the code above is selective refresh. The content of the element matching selector is replaced with the output of render_callback. Output the same element in the template as well.
<p class="footer-text"><?php echo esc_html( get_theme_mod( 'mytheme_footer_text', 'My Site' ) ); ?></p>
Elements with selective refresh get a pencil icon in the preview; clicking it opens the matching setting.
Changing the color with JavaScript
Load the JavaScript that runs inside the preview with the customize_preview_init hook. Specify customize-preview as a dependency.
<?php
add_action( 'customize_preview_init', function () {
wp_enqueue_script(
'mytheme-customizer',
get_theme_file_uri( 'js/customizer.js' ),
array( 'customize-preview' ),
wp_get_theme()->get( 'Version' ),
true
);
} );
// js/customizer.js
( function ( api ) {
api( 'mytheme_primary_color', function ( value ) {
value.bind( function ( newColor ) {
// Changing the CSS variable updates every element that uses it
document.documentElement.style.setProperty( '--color-primary', newColor );
} );
} );
} )( wp.customize );
Using the values on the front end
Read saved values with get_theme_mod( setting ID, default ). The second argument is returned when nothing has been saved yet, so make it the same as the default in add_setting().
Output the color as a CSS variable. This example assumes the theme's CSS is loaded under the handle mytheme-style.
<?php
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style( 'mytheme-style', get_stylesheet_uri(), array(), wp_get_theme()->get( 'Version' ) );
$color = sanitize_hex_color( get_theme_mod( 'mytheme_primary_color', '#2271b1' ) );
if ( $color ) {
wp_add_inline_style( 'mytheme-style', ':root { --color-primary: ' . $color . '; }' );
}
} );
In the theme's CSS, use the variable as in background-color: var(--color-primary);. How to use CSS variables is explained in the article on CSS custom properties.
Use the image and select box values in templates like this.
<?php
// Banner image (output an img tag from the attachment ID)
$banner_id = (int) get_theme_mod( 'mytheme_banner', 0 );
if ( $banner_id ) {
echo wp_get_attachment_image( $banner_id, 'large' );
}
// Add the sidebar position to the body classes
add_filter( 'body_class', function ( $classes ) {
$classes[] = 'layout-' . sanitize_html_class( get_theme_mod( 'mytheme_layout', 'sidebar-right' ) );
return $classes;
} );
Put the body_class filter in functions.php and the banner part in a template (such as header.php).
Checking
- Open Appearance → Customize and check that the "Theme Settings" panel and its two sections appear.
- Change the color and check that the preview changes without reloading; change the footer text and check that only that part changes.
- After clicking "Publish," open the front end while logged out and check that the values are applied.
Summary
- Add panels, sections, settings and controls with the
customize_registerhook. Keep everything in one callback. - Specify a
sanitize_callbackfor each setting, and let only values inchoicesthrough for select boxes. - When you use
postMessage, provide either selective refresh (add_partial()) or JavaScript loaded withcustomize_preview_init. - Read values with
get_theme_mod(), and output colors as CSS variables withwp_add_inline_style(). - With block themes, the Site Editor and
theme.json, not the Customizer, are the center of settings.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)