How to Use the WordPress Settings API

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.