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
- 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');