Building a plugin options page with the WordPress Settings API
The Settings API is WordPress’s built-in way to create options pages with proper validation, nonce handling, and storage in wp_options. It handles the plumbing so you don’t have to.
Problem: How do you create a plugin options page in WordPress with proper validation, nonce handling, and storage in the database?
Solution: A minimal but complete example — an options page with two fields:
// 1. Register settings, sections and fields
add_action( 'admin_init', 'myplugin_register_settings' );
function myplugin_register_settings() {
register_setting( 'myplugin_options', 'myplugin_options', 'myplugin_sanitize' );
add_settings_section(
'myplugin_general',
__( 'General Settings', 'textdomain' ),
null,
'myplugin'
);
add_settings_field(
'api_key',
__( 'API Key', 'textdomain' ),
'myplugin_field_api_key',
'myplugin',
'myplugin_general'
);
add_settings_field(
'enable_feature',
__( 'Enable Feature', 'textdomain' ),
'myplugin_field_enable',
'myplugin',
'myplugin_general'
);
}
function myplugin_field_api_key() {
$opts = get_option( 'myplugin_options', [] );
$val = $opts['api_key'] ?? '';
echo '<input type="text" name="myplugin_options[api_key]" value="' . esc_attr( $val ) . '" class="regular-text">';
}
function myplugin_field_enable() {
$opts = get_option( 'myplugin_options', [] );
$checked = ! empty( $opts['enable_feature'] ) ? 'checked' : '';
echo '<input type="checkbox" name="myplugin_options[enable_feature]" value="1" ' . $checked . '>';
}
function myplugin_sanitize( $input ) {
return [
'api_key' => sanitize_text_field( $input['api_key'] ?? '' ),
'enable_feature' => ! empty( $input['enable_feature'] ) ? 1 : 0,
];
}
// 2. Register the menu page
add_action( 'admin_menu', 'myplugin_add_page' );
function myplugin_add_page() {
add_options_page(
__( 'My Plugin Settings', 'textdomain' ),
__( 'My Plugin', 'textdomain' ),
'manage_options',
'myplugin',
'myplugin_render_page'
);
}
// 3. Render the page
function myplugin_render_page() {
if ( ! current_user_can( 'manage_options' ) ) return;
?>
<div class="wrap">
<h1><?php esc_html_e( 'My Plugin Settings', 'textdomain' ); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields( 'myplugin_options' );
do_settings_sections( 'myplugin' );
submit_button();
?>
</form>
</div>
<?php
}
NOTE: Always sanitize in the callback passed to register_setting(). The API handles nonces automatically — never add your own nonce field to a Settings API form.