How to Create a WordPress Plugin Settings Page (Step-by-Step)

Plugin settings pages allow users to configure your plugin without touching code. The WordPress Settings API provides a standardized, secure way to create settings pages that follow WordPress UI conventions and handle data properly.

Why Use the Settings API

The Settings API offers significant advantages over custom form handling:

Automatic security – Nonces and capability checks are built-in Consistent UI – Pages match WordPress admin styling automatically Data handling – WordPress manages saving, sanitization, and validation Best practices – Follows WordPress standards automatically

Avoid building custom form handlers. The Settings API is tested, secure, and maintained by WordPress core.

Creating the Admin Menu

First, add a menu page to WordPress admin:

function dprt_add_settings_page() {
    add_options_page(
        'My Plugin Settings',          // Page title
        'My Plugin',                   // Menu title
        'manage_options',              // Capability
        'my-plugin-settings',          // Menu slug
        'dprt_render_settings_page'    // Callback function
    );
}
add_action( 'admin_menu', 'dprt_add_settings_page' );

This adds a submenu under Settings. For a top-level menu, use add_menu_page() instead.

Registering Settings

Register each setting WordPress should manage:

function dprt_register_settings() {
    register_setting(
        'dprt_settings_group',       // Option group
        'dprt_api_key',              // Option name
        array(
            'type' => 'string',
            'sanitize_callback' => 'sanitize_text_field',
            'default' => ''
        )
    );

    register_setting(
        'dprt_settings_group',
        'dprt_enable_feature',
        array(
            'type' => 'boolean',
            'default' => false
        )
    );
}
add_action( 'admin_init', 'dprt_register_settings' );

Each setting gets a name (stored in wp_options), type, sanitization callback, and default value.

Creating Settings Sections

Organize settings into logical sections:

function dprt_register_settings_sections() {
    add_settings_section(
        'dprt_api_section',              // Section ID
        'API Configuration',             // Section title
        'dprt_api_section_callback',     // Section callback
        'my-plugin-settings'             // Page slug
    );

    add_settings_section(
        'dprt_display_section',
        'Display Options',
        'dprt_display_section_callback',
        'my-plugin-settings'
    );
}
add_action( 'admin_init', 'dprt_register_settings_sections' );

function dprt_api_section_callback() {
    echo '<p>Configure API settings for third-party integrations.</p>';
}

function dprt_display_section_callback() {
    echo '<p>Control how content displays on your site.</p>';
}

Sections group related settings with descriptive headers and instructions.

Adding Settings Fields

Add individual fields to sections:

function dprt_register_settings_fields() {
    // Text field
    add_settings_field(
        'dprt_api_key',                  // Field ID
        'API Key',                       // Field title
        'dprt_api_key_callback',         // Field callback
        'my-plugin-settings',            // Page slug
        'dprt_api_section'               // Section ID
    );

    // Checkbox field
    add_settings_field(
        'dprt_enable_feature',
        'Enable Feature',
        'dprt_enable_feature_callback',
        'my-plugin-settings',
        'dprt_display_section'
    );
}
add_action( 'admin_init', 'dprt_register_settings_fields' );

Field Callback Functions

Field callbacks render the HTML inputs:

// Text input
function dprt_api_key_callback() {
    $value = get_option( 'dprt_api_key', '' );
    echo '<input type="text" name="dprt_api_key" value="' . esc_attr( $value ) . '" class="regular-text">';
    echo '<p class="description">Enter your API key from the provider.</p>';
}

// Checkbox
function dprt_enable_feature_callback() {
    $value = get_option( 'dprt_enable_feature', false );
    echo '<input type="checkbox" name="dprt_enable_feature" value="1" ' . checked( 1, $value, false ) . '>';
    echo '<label>Enable this experimental feature</label>';
}

// Select dropdown
function dprt_display_style_callback() {
    $value = get_option( 'dprt_display_style', 'default' );
    $options = array(
        'default' => 'Default Style',
        'minimal' => 'Minimal Style',
        'fancy' => 'Fancy Style'
    );
    echo '<select name="dprt_display_style">';
    foreach ( $options as $key => $label ) {
        echo '<option value="' . esc_attr( $key ) . '" ' . selected( $value, $key, false ) . '>' . esc_html( $label ) . '</option>';
    }
    echo '</select>';
}

Always escape output and use WordPress helper functions like checked() and selected().

Rendering the Settings Page

The settings page displays all registered sections and fields:

function dprt_render_settings_page() {
    // Check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }

    // Show error/update messages
    settings_errors( 'dprt_messages' );
    ?>
    <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( 'dprt_settings_group' );

            // Output settings sections
            do_settings_sections( 'my-plugin-settings' );

            // Output submit button
            submit_button( 'Save Settings' );
            ?>
        </form>
    </div>
    <?php
}

settings_fields() outputs hidden nonce fields. do_settings_sections() renders all registered sections and fields. submit_button() creates the submit button.

Custom Sanitization

Implement custom sanitization for complex data:

function dprt_sanitize_email_list( $input ) {
    $emails = explode( ',', $input );
    $sanitized = array();

    foreach ( $emails as $email ) {
        $email = trim( $email );
        if ( is_email( $email ) ) {
            $sanitized[] = sanitize_email( $email );
        }
    }

    return implode( ', ', $sanitized );
}

register_setting(
    'dprt_settings_group',
    'dprt_email_list',
    array(
        'sanitize_callback' => 'dprt_sanitize_email_list'
    )
);

Sanitization callbacks clean data before saving to the database.

Validation and Error Messages

Add validation with admin notices:

function dprt_validate_api_key( $value ) {
    if ( strlen( $value ) < 32 ) {
        add_settings_error(
            'dprt_messages',
            'dprt_message',
            'API key must be at least 32 characters long.',
            'error'
        );
        // Return old value to prevent invalid save
        return get_option( 'dprt_api_key' );
    }
    return $value;
}

Tabbed Settings Interface

For complex plugins, organize settings into tabs:

function dprt_render_tabbed_settings_page() {
    $active_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general';
    ?>
    <div class="wrap">
        <h1>My Plugin Settings</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' : ''; ?>">General</a>
            <a href="?page=my-plugin-settings&tab=advanced" class="nav-tab <?php echo $active_tab === 'advanced' ? 'nav-tab-active' : ''; ?>">Advanced</a>
        </h2>
        <form action="options.php" method="post">
            <?php
            if ( $active_tab === 'general' ) {
                settings_fields( 'dprt_general_settings' );
                do_settings_sections( 'dprt-general-settings' );
            } else {
                settings_fields( 'dprt_advanced_settings' );
                do_settings_sections( 'dprt-advanced-settings' );
            }
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

Retrieving Saved Options

Access saved settings anywhere in your plugin:

// Single option
$api_key = get_option( 'dprt_api_key', '' );

// Multiple options
$options = array(
    'api_key' => get_option( 'dprt_api_key', '' ),
    'enable_feature' => get_option( 'dprt_enable_feature', false ),
    'display_style' => get_option( 'dprt_display_style', 'default' )
);

Always provide default values as the second parameter.

Conclusion

The WordPress Settings API streamlines plugin configuration. Register settings, create sections and fields, render the page with built-in functions, and WordPress handles security, validation, and data storage. This approach creates professional, secure settings pages that integrate seamlessly with WordPress admin.

  • Real-world settings page examples

Includes complete code examples, form templates, and best practices for creating user-friendly plugin settings interfaces.

  1. Settings API Documentation
  2. Administration Menus
  3. Options API Reference
  4. Data Validation
  5. Admin Notices

Call to Action

Supercharge your development! ACF Copilot Pro generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!