Settings API
The WordPress Settings API provides a standardized way to create and manage settings pages. It handles form generation, validation, sanitization, and storage.
Core Concepts
- Settings – Individual options stored in the database
- Sections – Groups of related settings on a page
- Fields – Individual form inputs within sections
- Pages – Admin pages that display sections and fields
Core Functions
register_setting()
Registers a setting and its sanitization callback.
register_setting(
string $option_group,
string $option_name,
array $args = array()
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
$option_group |
string | Settings group name (matches page slug) |
$option_name |
string | Option name in wp_options table |
$args |
array | Configuration arguments |
Args:
| Key | Type | Description |
|---|---|---|
type |
string | Data type (‘string’, ‘boolean’, ‘integer’, ‘number’, ‘array’, ‘object’) |
description |
string | Setting description (for REST API) |
sanitize_callback |
callable | Sanitization function |
show_in_rest |
bool/array | Whether to expose in REST API |
default |
mixed | Default value |
add_settings_section()
Adds a section to a settings page.
add_settings_section(
string $id,
string $title,
callable $callback,
string $page,
array $args = array()
);
add_settings_field()
Adds a field to a settings section.
add_settings_field(
string $id,
string $title,
callable $callback,
string $page,
string $section = 'default',
array $args = array()
);
settings_fields()
Outputs security fields for a settings page (nonce, action, etc.).
settings_fields( string $option_group );
do_settings_sections()
Outputs all sections and fields for a page.
do_settings_sections( string $page );
do_settings_fields()
Outputs fields for a specific section.
do_settings_fields( string $page, string $section );
Complete Example
Step 1: Create Settings Page
add_action( 'admin_menu', 'my_plugin_add_settings_page' );
function my_plugin_add_settings_page() {
add_options_page(
__( 'My Plugin Settings' ), // Page title
__( 'My Plugin' ), // Menu title
'manage_options', // Capability
'my-plugin-settings', // Menu slug
'my_plugin_settings_page' // Callback
);
}
function my_plugin_settings_page() {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form action="options.php" method="post">
<?php
// Output security fields
settings_fields( 'my_plugin_options' );
// Output sections and fields
do_settings_sections( 'my-plugin-settings' );
// Submit button
submit_button();
?>
</form>
</div>
<?php
}
Step 2: Register Settings
add_action( 'admin_init', 'my_plugin_register_settings' );
function my_plugin_register_settings() {
// Register the setting
register_setting(
'my_plugin_options', // Option group
'my_plugin_settings', // Option name
array(
'type' => 'array',
'sanitize_callback' => 'my_plugin_sanitize_settings',
'default' => array(
'text_field' => '',
'checkbox_field' => false,
'select_field' => 'option1',
),
)
);
// Add section
add_settings_section(
'my_plugin_general_section', // Section ID
__( 'General Settings' ), // Title
'my_plugin_section_callback', // Callback
'my-plugin-settings' // Page slug
);
// Add fields
add_settings_field(
'text_field', // Field ID
__( 'Text Field' ), // Title
'my_plugin_text_field_callback', // Callback
'my-plugin-settings', // Page slug
'my_plugin_general_section', // Section ID
array(
'label_for' => 'text_field',
'class' => 'my-plugin-row',
)
);
add_settings_field(
'checkbox_field',
__( 'Checkbox Field' ),
'my_plugin_checkbox_field_callback',
'my-plugin-settings',
'my_plugin_general_section'
);
add_settings_field(
'select_field',
__( 'Select Field' ),
'my_plugin_select_field_callback',
'my-plugin-settings',
'my_plugin_general_section',
array( 'label_for' => 'select_field' )
);
}
Step 3: Section and Field Callbacks
function my_plugin_section_callback( $args ) {
?>
<p><?php _e( 'Configure the general settings for my plugin.' ); ?></p>
<?php
}
function my_plugin_text_field_callback( $args ) {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['text_field'] ) ? $options['text_field'] : '';
?>
<input type="text"
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="my_plugin_settings[text_field]"
value="<?php echo esc_attr( $value ); ?>"
class="regular-text">
<p class="description">
<?php _e( 'Enter some text here.' ); ?>
</p>
<?php
}
function my_plugin_checkbox_field_callback( $args ) {
$options = get_option( 'my_plugin_settings' );
$checked = isset( $options['checkbox_field'] ) ? $options['checkbox_field'] : false;
?>
<label>
<input type="checkbox"
name="my_plugin_settings[checkbox_field]"
value="1"
<?php checked( $checked, true ); ?>>
<?php _e( 'Enable this feature' ); ?>
</label>
<?php
}
function my_plugin_select_field_callback( $args ) {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['select_field'] ) ? $options['select_field'] : 'option1';
?>
<select id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="my_plugin_settings[select_field]">
<option value="option1" <?php selected( $value, 'option1' ); ?>>
<?php _e( 'Option 1' ); ?>
</option>
<option value="option2" <?php selected( $value, 'option2' ); ?>>
<?php _e( 'Option 2' ); ?>
</option>
<option value="option3" <?php selected( $value, 'option3' ); ?>>
<?php _e( 'Option 3' ); ?>
</option>
</select>
<?php
}
Step 4: Sanitization Callback
function my_plugin_sanitize_settings( $input ) {
$sanitized = array();
// Text field
if ( isset( $input['text_field'] ) ) {
$sanitized['text_field'] = sanitize_text_field( $input['text_field'] );
}
// Checkbox (boolean)
$sanitized['checkbox_field'] = ! empty( $input['checkbox_field'] );
// Select field
$valid_options = array( 'option1', 'option2', 'option3' );
if ( isset( $input['select_field'] ) && in_array( $input['select_field'], $valid_options, true ) ) {
$sanitized['select_field'] = $input['select_field'];
} else {
$sanitized['select_field'] = 'option1';
}
return $sanitized;
}
Advanced Field Types
Textarea
function textarea_field_callback() {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['textarea_field'] ) ? $options['textarea_field'] : '';
?>
<textarea name="my_plugin_settings[textarea_field]"
rows="5"
cols="50"
class="large-text code"><?php echo esc_textarea( $value ); ?></textarea>
<?php
}
Number Field
function number_field_callback() {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['number_field'] ) ? $options['number_field'] : 0;
?>
<input type="number"
name="my_plugin_settings[number_field]"
value="<?php echo esc_attr( $value ); ?>"
min="0"
max="100"
step="1"
class="small-text">
<?php
}
Radio Buttons
function radio_field_callback() {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['radio_field'] ) ? $options['radio_field'] : 'choice1';
?>
<fieldset>
<label>
<input type="radio"
name="my_plugin_settings[radio_field]"
value="choice1"
<?php checked( $value, 'choice1' ); ?>>
<?php _e( 'Choice 1' ); ?>
</label><br>
<label>
<input type="radio"
name="my_plugin_settings[radio_field]"
value="choice2"
<?php checked( $value, 'choice2' ); ?>>
<?php _e( 'Choice 2' ); ?>
</label><br>
<label>
<input type="radio"
name="my_plugin_settings[radio_field]"
value="choice3"
<?php checked( $value, 'choice3' ); ?>>
<?php _e( 'Choice 3' ); ?>
</label>
</fieldset>
<?php
}
Color Picker
add_action( 'admin_enqueue_scripts', 'enqueue_color_picker' );
function enqueue_color_picker( $hook ) {
if ( 'settings_page_my-plugin-settings' !== $hook ) {
return;
}
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
wp_add_inline_script( 'wp-color-picker', "
jQuery(document).ready(function($) {
$('.my-color-picker').wpColorPicker();
});
" );
}
function color_field_callback() {
$options = get_option( 'my_plugin_settings' );
$value = isset( $options['color_field'] ) ? $options['color_field'] : '#ffffff';
?>
<input type="text"
name="my_plugin_settings[color_field]"
value="<?php echo esc_attr( $value ); ?>"
class="my-color-picker"
data-default-color="#ffffff">
<?php
}
Media/Image Upload
function image_field_callback() {
$options = get_option( 'my_plugin_settings' );
$image_id = isset( $options['image_field'] ) ? $options['image_field'] : 0;
$image = $image_id ? wp_get_attachment_image_url( $image_id, 'thumbnail' ) : '';
?>
<div class="image-preview">
<?php if ( $image ) : ?>
<img src="<?php echo esc_url( $image ); ?>" style="max-width: 150px;">
<?php endif; ?>
</div>
<input type="hidden"
name="my_plugin_settings[image_field]"
id="image_field"
value="<?php echo esc_attr( $image_id ); ?>">
<button type="button" class="button" id="upload_image">
<?php _e( 'Select Image' ); ?>
</button>
<button type="button" class="button" id="remove_image"
style="<?php echo $image_id ? '' : 'display:none;'; ?>">
<?php _e( 'Remove' ); ?>
</button>
<?php
}
Multiple Sections
add_action( 'admin_init', 'register_multiple_sections' );
function register_multiple_sections() {
// General section
add_settings_section(
'general_section',
__( 'General' ),
'general_section_callback',
'my-plugin-settings'
);
// Display section
add_settings_section(
'display_section',
__( 'Display' ),
'display_section_callback',
'my-plugin-settings'
);
// Advanced section
add_settings_section(
'advanced_section',
__( 'Advanced' ),
'advanced_section_callback',
'my-plugin-settings'
);
}
Tabbed Settings Page
function my_plugin_tabbed_settings_page() {
$active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'general';
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<h2 class="nav-tab-wrapper">
<a href="?page=my-plugin-settings&tab=general"
class="nav-tab <?php echo $active_tab === 'general' ? 'nav-tab-active' : ''; ?>">
<?php _e( 'General' ); ?>
</a>
<a href="?page=my-plugin-settings&tab=display"
class="nav-tab <?php echo $active_tab === 'display' ? 'nav-tab-active' : ''; ?>">
<?php _e( 'Display' ); ?>
</a>
<a href="?page=my-plugin-settings&tab=advanced"
class="nav-tab <?php echo $active_tab === 'advanced' ? 'nav-tab-active' : ''; ?>">
<?php _e( 'Advanced' ); ?>
</a>
</h2>
<form action="options.php" method="post">
<?php
switch ( $active_tab ) {
case 'display':
settings_fields( 'my_plugin_display_options' );
do_settings_sections( 'my-plugin-display' );
break;
case 'advanced':
settings_fields( 'my_plugin_advanced_options' );
do_settings_sections( 'my-plugin-advanced' );
break;
default:
settings_fields( 'my_plugin_general_options' );
do_settings_sections( 'my-plugin-general' );
}
submit_button();
?>
</form>
</div>
<?php
}
Settings Errors
add_settings_error()
Display error/success messages.
add_settings_error(
string $setting,
string $code,
string $message,
string $type = 'error' // 'error', 'success', 'warning', 'info'
);
Example in Sanitization:
function my_plugin_sanitize_settings( $input ) {
$sanitized = array();
if ( empty( $input['required_field'] ) ) {
add_settings_error(
'my_plugin_settings',
'required_field_error',
__( 'Required field cannot be empty.' ),
'error'
);
// Keep old value
$old = get_option( 'my_plugin_settings' );
$sanitized['required_field'] = $old['required_field'] ?? '';
} else {
$sanitized['required_field'] = sanitize_text_field( $input['required_field'] );
}
return $sanitized;
}
settings_errors()
Display registered errors:
function my_plugin_settings_page() {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php settings_errors(); ?>
<form action="options.php" method="post">
<!-- ... -->
</form>
</div>
<?php
}
REST API Integration
register_setting( 'my_plugin_options', 'my_plugin_api_setting', array(
'type' => 'string',
'show_in_rest' => true,
'default' => '',
) );
// Or with detailed schema
register_setting( 'my_plugin_options', 'my_plugin_complex_setting', array(
'type' => 'object',
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'enabled' => array(
'type' => 'boolean',
),
'count' => array(
'type' => 'integer',
),
),
),
),
) );
Hooks
| Hook | Description |
|---|---|
pre_update_option_{option} |
Before option is updated |
update_option_{option} |
After option is updated |
add_option_{option} |
When option is first added |
admin_init |
When to register settings |
Best Practices
- Group Related Settings: Use a single option for related settings (array)
- Sanitize Everything: Always validate and sanitize input
- Use Nonces:
settings_fields()handles this automatically - Provide Defaults: Use
defaultinregister_setting() - Check Capabilities: Use appropriate capability for menu page
- Escape Output: Always escape when displaying values