Options Page

Option Setup

/**
 * Create the top-level menu item
 */
function my_plugin_add_menu_page() {
    add_menu_page(
        'My Plugin Settings',       // Page title
        'My Plugin',               // Menu title
        'manage_options',          // Capability
        'my-plugin-settings',      // Menu slug
        'my_plugin_render_page',   // Callback function
        'dashicons-admin-generic', // Icon
        80                         // Position
    );
}
add_action('admin_menu', 'my_plugin_add_menu_page');

/**
 * Register settings
 */
function my_plugin_register_settings() {
    register_setting('my_plugin_options', 'my_plugin_options');
    
    // General Settings Section
    add_settings_section(
        'general',
        'General Settings',
        '__return_false',
        'my-plugin-settings'
    );
    
    add_settings_field(
        'api_key',
        'API Key',
        'my_plugin_text_field_cb',
        'my-plugin-settings',
        'general',
        ['label_for' => 'api_key']
    );
}
add_action('admin_init', 'my_plugin_register_settings');

/**
 * Render text field
 */
function my_plugin_text_field_cb($args) {
    $options = get_option('my_plugin_options');
    echo '<input type="text" id="'.$args['label_for'].'" name="my_plugin_options['.$args['label_for'].']" value="'.esc_attr($options[$args['label_for']] ?? '').'" class="regular-text">';
}

/**
 * Basic page render
 */
function my_plugin_render_page() {
    if (!current_user_can('manage_options')) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
        <form action="options.php" method="post">
            <?php
            settings_fields('my_plugin_options');
            do_settings_sections('my-plugin-settings');
            submit_button('Save Settings');
            ?>
        </form>
    </div>
    <?php
}

Alt Version with Tab

/**
 * Tabbed options page
 */
function my_plugin_render_tabbed_page() {
    if (!current_user_can('manage_options')) {
        return;
    }
    
    // Define tabs
    $tabs = [
        'general' => 'General',
        'advanced' => 'Advanced',
        'licenses' => 'Licenses'
    ];
    
    $current_tab = $_GET['tab'] ?? 'general';
    ?>
    <div class="wrap">
        <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
        
        <nav class="nav-tab-wrapper">
            <?php foreach ($tabs as $tab => $name) : ?>
                <a href="?page=my-plugin-settings&tab=<?php echo esc_attr($tab); ?>" 
                   class="nav-tab <?php echo $current_tab === $tab ? 'nav-tab-active' : ''; ?>">
                    <?php echo esc_html($name); ?>
                </a>
            <?php endforeach; ?>
        </nav>
        
        <div class="tab-content">
            <form method="post" action="options.php">
                <?php
                // Output the correct settings based on tab
                switch ($current_tab) {
                    case 'advanced':
                        settings_fields('my_plugin_advanced_options');
                        do_settings_sections('my-plugin-settings-advanced');
                        break;
                        
                    case 'licenses':
                        settings_fields('my_plugin_license_options');
                        do_settings_sections('my-plugin-settings-licenses');
                        break;
                        
                    default: // General
                        settings_fields('my_plugin_options');
                        do_settings_sections('my-plugin-settings');
                        break;
                }
                
                submit_button('Save Settings');
                ?>
            </form>
        </div>
    </div>
    <?php
}

// Update the menu page callback
function my_plugin_add_menu_page() {
    add_menu_page(
        'My Plugin Settings',
        'My Plugin',
        'manage_options',
        'my-plugin-settings',
        'my_plugin_render_tabbed_page', // Updated callback
        'dashicons-admin-generic',
        80
    );
}

/**
 * Register all settings sections and fields
 */
function my_plugin_register_all_settings() {
    // General Settings
    register_setting('my_plugin_options', 'my_plugin_options');
    add_settings_section('general', '', '__return_false', 'my-plugin-settings');
    add_settings_field('api_key', 'API Key', 'my_plugin_text_field_cb', 'my-plugin-settings', 'general', ['label_for' => 'api_key']);
    
    // Advanced Settings
    register_setting('my_plugin_advanced_options', 'my_plugin_advanced_options');
    add_settings_section('advanced', '', '__return_false', 'my-plugin-settings-advanced');
    add_settings_field('cache_ttl', 'Cache TTL', 'my_plugin_number_field_cb', 'my-plugin-settings-advanced', 'advanced', ['label_for' => 'cache_ttl']);
    
    // License Settings
    register_setting('my_plugin_license_options', 'my_plugin_license_options');
    add_settings_section('licenses', '', '__return_false', 'my-plugin-settings-licenses');
    add_settings_field('license_key', 'License Key', 'my_plugin_license_field_cb', 'my-plugin-settings-licenses', 'licenses', ['label_for' => 'license_key']);
}
add_action('admin_init', 'my_plugin_register_all_settings');

/**
 * Additional field callbacks
 */
function my_plugin_number_field_cb($args) {
    $options = get_option('my_plugin_advanced_options');
    echo '<input type="number" id="'.$args['label_for'].'" name="my_plugin_advanced_options['.$args['label_for'].']" value="'.esc_attr($options[$args['label_for']] ?? '').'" class="small-text"> seconds';
}

function my_plugin_license_field_cb($args) {
    $options = get_option('my_plugin_license_options');
    echo '<input type="password" id="'.$args['label_for'].'" name="my_plugin_license_options['.$args['label_for'].']" value="'.esc_attr($options[$args['label_for']] ?? '').'" class="regular-text">';
    echo '<p class="description">Enter your license key to receive updates</p>';
}

Version with tabs like woocommerce

function my_plugin_render_wc_style_page() {
    if (!current_user_can('manage_options')) {
        return;
    }
    
    $tabs = [
        'general' => '<span class="dashicons dashicons-admin-settings"></span> General',
        'display' => '<span class="dashicons dashicons-layout"></span> Display',
        'api' => '<span class="dashicons dashicons-rest-api"></span> API'
    ];
    
    $current_tab = $_GET['tab'] ?? 'general';
    ?>
    <div class="wrap woocommerce">
        <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
        
        <div class="wc-tabs-back"></div>
        
        <nav class="nav-tab-wrapper woo-nav-tab-wrapper">
            <?php foreach ($tabs as $tab => $name) : ?>
                <a href="?page=my-plugin-settings&tab=<?php echo esc_attr($tab); ?>" 
                   class="nav-tab <?php echo $current_tab === $tab ? 'nav-tab-active' : ''; ?>">
                    <?php echo wp_kses_post($name); ?>
                </a>
            <?php endforeach; ?>
        </nav>
        
        <div class="wc-tab-content">
            <form method="post" action="options.php">
                <table class="form-table">
                    <?php
                    switch ($current_tab) {
                        case 'display':
                            settings_fields('my_plugin_display_options');
                            do_settings_sections('my-plugin-settings-display');
                            break;
                            
                        case 'api':
                            settings_fields('my_plugin_api_options');
                            do_settings_sections('my-plugin-settings-api');
                            break;
                            
                        default:
                            settings_fields('my_plugin_options');
                            do_settings_sections('my-plugin-settings');
                            break;
                    }
                    ?>
                </table>
                <?php submit_button(); ?>
            </form>
        </div>
    </div>
    
    <style>
    .wc-tabs-back {
        background: #f1f1f1;
        height: 61px;
        margin: -23px -20px 20px;
        border-bottom: 1px solid #ccc;
    }
    .woo-nav-tab-wrapper {
        margin-top: -61px;
        padding-left: 20px;
    }
    .wc-tab-content {
        background: #fff;
        border: 1px solid #ccc;
        border-top: 0;
        padding: 20px;
        margin: 0 -20px;
    }
    </style>
    <?php
}

Pro Tips for Options Pages

  1. Security Best Practices:
    • Always use current_user_can() checks
    • Use esc_attr(), esc_html(), and wp_kses_post() for output
    • Validate and sanitize all inputs
// Group related options
register_setting('my_plugin_group', 'option_name');

// Use sections logically
add_settings_section('section_id', 'Section Title', 'callback', 'page_slug');

Tabs like woocommerce

function my_plugin_add_help_tab() {
    $screen = get_current_screen();
    
    $screen->add_help_tab([
        'id' => 'my-plugin-help',
        'title' => 'Plugin Help',
        'content' => '<p>This is where you configure all plugin settings</p>'
    ]);
}
add_action('load-toplevel_page_my-plugin-settings', 'my_plugin_add_help_tab');