How to create custom settings pages in WordPress
You can create a custom WordPress settings page without a plugin by adding an admin menu item, registering settings and fields, then rendering a form through the Settings API. For a small page, code is fine; for validation, imports and many field types, a plugin is faster.
- 01Plan the settings structure
- 02Add the admin page
- 03Register settings and fields
- 04Render the settings form
- 05Secure and test every path
- 06Use the fast way when needed
What you need
- A WordPress development or staging site
- Plugin or theme code access
- Administrator access for testing
- A basic understanding of PHP
Plan the settings structure
Decide whether the page belongs under Settings or needs its own top-level admin menu. Use a unique page slug, an option name and a capability that matches the people who should manage the values.
Keep related values in one option array when they belong together, or use separate options when they have different lifecycles. Write down each field's expected type before you code its sanitisation.
Add the admin page
Hook your menu registration to admin_menu. Use add_options_page() for a page under Settings, or add_menu_page() for a new top-level menu. Both functions take a capability, slug and callback. WordPress uses that capability to decide whether the menu is visible, but your callback should still check access before outputting sensitive content.
Do not register the menu on admin_init. Calling menu functions too early is a common cause of the “You do not have sufficient permissions” error.
Register settings and fields
Hook a registration function to admin_init. Call register_setting() for the option group and option name, then add sections with add_settings_section() and fields with add_settings_field(). The Settings API expects these registration calls on admin_init.
Give register_setting() a sanitisation callback. Validate known values such as booleans, numbers and allowed choices instead of blindly saving input. Sanitisation is a fallback when stricter validation is not practical.
When using one option array, make each input name use that option name with a field key inside it, such as my_plugin_options[api_key]. A field that is not registered, or whose name does not match the registered option structure, is a frequent reason for values not being saved.
Render the settings form
In the page callback, output a normal admin wrapper and a form that posts to options.php. Inside the form, call settings_fields() with the same option group passed to register_setting(), then call do_settings_sections() with the same page slug used when adding the fields, followed by submit_button(). These functions output the hidden security fields, registered sections, fields and save button.
Each field callback should read the current value with get_option(), escape it for the relevant HTML context and echo the input markup. For checkboxes, remember that an unchecked box sends no value; your sanitisation callback should convert missing values to a predictable false or zero.
Secure and test every path
The Settings API supplies a nonce through settings_fields(), and options.php performs capability checks when saving. Do not treat a nonce as authorisation: use an appropriate capability for the page and, where you handle custom actions, check current_user_can() as well.
- Open the page as an administrator and save valid values.
- Try invalid values and confirm the sanitisation callback rejects or normalises them.
- Check that the values remain after a page reload and can be read with
get_option(). - Test a user without the required capability.
- On multisite, remember that Settings API submissions normally require the appropriate network-level permissions, with Super Admin access commonly required.
If the page is blank, check that the page slug passed to do_settings_sections() matches the page value passed to add_settings_section() and add_settings_field(). If the page saves but values disappear, check the option group, option name and input names first.
Use the fast way when needed
The manual route is reasonable for one small settings page with a few text fields, a checkbox and a simple sanitisation callback. It becomes maintenance work when you need repeatable field definitions, validation rules, imports, exports, role-based options or a large set of field types.
For that case, MB Settings Page is a practical shortcut. It builds admin settings pages with Meta Box fields and includes validation, imports and role-based options, so you do not have to maintain the Settings API form and field callbacks yourself. Install and activate it, create the settings page from its admin interface, add the fields, set access rules and test the saved values before using them on the front end.
Let MB Settings Page do it
Builds admin settings pages with Meta Box fields, validation, imports, and role-based options instead of coding them manually.
Sources
- developer.wordpress.org /reference/functions/add_menu_page/?utm_source=openai
- developer.wordpress.org /plugins/settings/using-settings-api/?utm_source=openai
- developer.wordpress.org /apis/security/sanitizing/?utm_source=openai
- developer.wordpress.org /apis/settings/?utm_source=openai
- developer.wordpress.org /reference/functions/settings_fields/?utm_source=openai
Questions
- Can I create a custom settings page without a plugin?
- Yes, a small custom settings page can be built with WordPress core. Add the admin page on <code>admin_menu</code>, register the option and fields on <code>admin_init</code>, then render the form with <code>settings_fields()</code>, <code>do_settings_sections()</code> and <code>submit_button()</code>. A plugin is mainly useful when you need a visual builder, many field types or advanced validation and import features.
- Why are my WordPress settings not saving?
- Settings usually fail to save because the option was not registered, the form uses the wrong option group, or the input name does not match the registered option. Check that <code>register_setting()</code> runs on <code>admin_init</code>, that <code>settings_fields()</code> uses the same group, and that the field is registered and named under the correct option.
- Which hook should register a custom settings page?
- Register the admin menu on <code>admin_menu</code>. Use <code>add_options_page()</code> for a page under Settings or <code>add_menu_page()</code> for a top-level page. Register the settings, sections and fields separately on <code>admin_init</code>; mixing these hooks is a common source of permission and missing-field errors.
- Do I need to add my own nonce to a Settings API form?
- Not normally. Calling <code>settings_fields()</code> adds the hidden option, action and nonce fields required by the standard Settings API form. You still need a suitable capability check because WordPress nonces help prevent request forgery but do not provide authentication or authorisation. Add a separate nonce when you create a custom form action outside the normal Settings API flow.