WordPress Customizer API Example: Add a Theme Setting Safely
Build a safe WordPress Customizer setting with sanitization, preview JavaScript, front-end output, and production checks.
Published
April 23, 2026
Reading Time
3 min read
Updated
April 23, 2026

Implementation Notes
Extension points, code paths, and implementation choices that should survive contact with production.
Best For
WordPress developers, agencies, and technical teams building custom plugin or theme functionality with cleaner operational defaults.
Primary Topics
Editorial Focus
Build Pattern: Extension points, code paths, and implementation choices that should survive contact with production. Updated on April 23, 2026.
Full Report
Last reviewed: April 23, 2026
The WordPress Customizer is still useful for classic themes, hybrid themes, and client sites that need safe visual controls without giving editors direct access to PHP files. The mistake is treating Customizer values like trusted configuration just because they come from the admin screen.
This guide shows a production-ready Customizer setting for a theme accent color. It covers capability checks, sanitization, selective refresh, front-end output, and the failure modes that usually make theme settings fragile.
When the Customizer is the right tool
Use the Customizer when a site owner needs to adjust theme behavior or presentation while previewing the result. Good examples are brand colors, footer text, header behavior, archive layout density, and promotional banner toggles. Avoid it for secrets, integration credentials, or plugin settings that are not theme-specific.
Register a setting, section, and control
<?php
add_action( 'customize_register', 'vulnwp_register_theme_customizer_settings' );
function vulnwp_register_theme_customizer_settings( WP_Customize_Manager $wp_customize ) {
$wp_customize->add_section(
'vulnwp_branding',
array(
'title' => __( 'Branding', 'vulnwp' ),
'description' => __( 'Theme-level brand controls.', 'vulnwp' ),
'priority' => 35,
)
);
$wp_customize->add_setting(
'vulnwp_accent_color',
array(
'default' => '#0f513d',
'type' => 'theme_mod',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'sanitize_hex_color',
'transport' => 'postMessage',
)
);
$wp_customize->add_control(
new WP_Customize_Color_Control(
$wp_customize,
'vulnwp_accent_color_control',
array(
'label' => __( 'Accent color', 'vulnwp' ),
'section' => 'vulnwp_branding',
'settings' => 'vulnwp_accent_color',
)
)
);
}
The setting uses theme_mod, which fits theme-specific values. The sanitize_callback is not optional: it prevents unexpected values from being stored and later printed into CSS.
Print the value safely on the front end
add_action( 'wp_head', 'vulnwp_print_accent_color_css' );
function vulnwp_print_accent_color_css() {
$accent = get_theme_mod( 'vulnwp_accent_color', '#0f513d' );
$accent = sanitize_hex_color( $accent );
if ( ! $accent ) {
$accent = '#0f513d';
}
printf(
'<style id="vulnwp-accent-color">:root{--vulnwp-accent:%s;}</style>' . "\n",
esc_html( $accent )
);
}
Even though the value was sanitized on save, the render path sanitizes again. That defensive pattern matters because values can be changed by imports, database edits, or old code paths.
Add live preview JavaScript
add_action( 'customize_preview_init', 'vulnwp_enqueue_customizer_preview' );
function vulnwp_enqueue_customizer_preview() {
wp_enqueue_script(
'vulnwp-customizer-preview',
get_theme_file_uri( 'assets/js/customizer-preview.js' ),
array( 'customize-preview' ),
wp_get_theme()->get( 'Version' ),
true
);
}
( function( wp ) {
wp.customize( 'vulnwp_accent_color', function( value ) {
value.bind( function( nextColor ) {
document.documentElement.style.setProperty( '--vulnwp-accent', nextColor );
} );
} );
} )( window.wp );
transport is set to postMessage, so the preview updates without a full page refresh. Keep the JavaScript small and limited to preview behavior.
Use the CSS variable in the theme
.button,
.wp-block-button__link {
background: var(--vulnwp-accent);
border-color: var(--vulnwp-accent);
}
a {
color: var(--vulnwp-accent);
}
Printing one CSS custom property keeps the dynamic output narrow. Avoid generating large inline stylesheets from Customizer values.
Production checklist
- Use
edit_theme_optionsunless a narrower capability is intentionally registered. - Add a
sanitize_callbackfor every setting. - Escape values again when printing them.
- Use
theme_modfor theme-specific values andoptiononly when the setting should survive theme switches. - Do not store secrets, API keys, or private tokens in Customizer settings.
- Test the preview and the saved front-end output separately.
Common mistakes
- Registering controls without sanitization. Admin UI is not a security boundary.
- Using Customizer for plugin configuration. Plugin settings usually belong in the Settings API or a dedicated plugin screen.
- Printing raw values into CSS. Always sanitize and escape on output.
- Forgetting defaults. A missing setting should not break the theme.
- Assuming block themes need the same workflow. Modern block themes often use
theme.jsonand global styles instead.
Related reading
If this setting controls theme assets, pair it with the wp_enqueue_script example. For plugin settings, use the Settings API example instead of forcing everything into the Customizer.


