Skip to content
⌨️ How to add your own section to the WordPress settings page

⌨️ How to add your own section to the WordPress settings page

Standard WordPress settings pages, "General", "Reading", "Discussion", cover a site's basic needs. But as soon as you write a plugin or customize a theme for a client, the built-in field set is no longer enough. You need a block with your own options: an API key, footer text, a mode toggle.

Creating a separate admin page for just two fields is overkill. It is much cleaner to add your own section to an existing settings page, right where the default WordPress options live. The Settings API provides a single function for this: add_settings_section.

Let's walk through its syntax, parameters, and build a working example with a section and a field, from the admin_init hook to output in the admin panel. No unnecessary abstractions: only what you can copy and run yourself.

💡 Quick overview:

  • Define the target page (general, reading, writing, discussion, media, or a custom one), you will need its slug in the fourth parameter
  • Register the section via add_settings_section() on the admin_init hook: provide an ID, title, and a callback for descriptive text
  • Attach fields to the section via add_settings_field(), and save the option itself via register_setting()
  • Call do_settings_sections() in the page callback for custom pages, the section will render automatically

What the Settings API is and why you need it

The Settings API appeared in WordPress 2.7 and has remained the standard way to add settings to the admin panel ever since. The idea is simple: instead of manually outputting a <form> and handling $_POST, you describe sections and fields through functions, and WordPress itself renders the markup, handles saving, and verifies the nonce.

The three pillars of the Settings API:

  • register_setting(), registers an option in wp_options;
  • add_settings_section(), creates a block (section) with a title;
  • add_settings_field(), adds a specific field to a section.

It is add_settings_section that is responsible for the "container", the visual block that groups several fields under a common heading on the settings page. Without it, there is simply nowhere to put the fields.

Add_settings_section syntax

The function signature is concise, four required parameters and one optional array:

1add_settings_section(
2 string $id,
3 string $title,
4 callable $callback,
5 string $page,
6 array $args = array()
7);

The function returns nothing. It registers the section in the global $wp_settings_sections array, from which WordPress retrieves it when do_settings_sections() is called on the target page. If you call add_settings_section without a subsequent do_settings_sections on a custom page, the section silently does not render.

An important nuance: add_settings_section only describes the block. The fields themselves are added by separate add_settings_field calls, specifying the section ID in the fifth parameter. Without fields, the section remains an empty wrapper, which is sometimes useful for a text explanation, but in practice at least one field is almost always placed next to it.

Function parameters, what to pass

$id (string, required), the unique slug of the section. Use lowercase letters, numbers, and underscores: my_plugin_main_section. You will specify this same ID in add_settings_field to "attach" the field to the section, and it also appears in the id HTML attribute of the wrapper tag.

$title (string, required), the section title that the administrator sees. It is output as an <h2> inside the settings page. Write it in plain language for the admin: «Настройки интеграции с CRM», not my_plugin_crm_settings.

$callback (callable, required), the name of the function that echoes the descriptive text between the section title and the fields. Signature: function my_callback($args), where $args is an array with the keys id, title, and callback. If no explanation is needed, pass '__return_false'.

$page (string, required), the slug of the settings page where the section is added. Built-in WordPress pages: general, reading, writing, discussion, media. For a custom page, the slug passed to add_options_page(). ⚠ The misc and privacy pages have been removed from core (deprecated), WordPress will automatically redirect miscgeneral, privacyreading, but it is better not to rely on this behavior.

$args (array, optional, since WordPress 6.1.0), an array for customizing the section's HTML wrapper:

Key

Type

Description

before_section

string

HTML inserted BEFORE the section content. Receives the section class as %s

after_section

string

HTML after the section content. Not output if the section is empty

section_class

string

CSS class for the section wrapper

The $args argument only works when do_settings_sections() is called. If do_settings_fields() is used by mistake, the array is ignored, and before_section/after_section silently have no effect.

Practical example: a section with a field on the "General" page

Let's put together a ready-to-use snippet for functions.php or your own plugin. The code adds a "Business card site settings" section to the options-general.php page with one text field, for example, for a phone number in the footer.

1/**
2 * Adds a custom section and field to the «General» page.
3 */
4function sdstudio_add_visiting_card_section() {
5 // 1. Section
6 add_settings_section(
7 'visiting_card_section', // Section ID
8 'Visiting Card Settings', // Title
9 'sdstudio_visiting_card_section_cb', // Callback explanation
10 'general', // «General» page
11 array(
12 'before_section' => '<div class="visiting-card-wrapper">',
13 'after_section' => '</div>',
14 )
15 );
16
17 // 2. Field
18 add_settings_field(
19 'footer_phone', // Field ID
20 'Footer Phone Number', // Label
21 'sdstudio_footer_phone_field_cb', // Callback — renders <input>
22 'general', // Same page
23 'visiting_card_section' // Which section to attach to
24 );
25
26 // 3. Registering the option
27 register_setting( 'general', 'footer_phone' );
28}
29add_action( 'admin_init', 'sdstudio_add_visiting_card_section' );
30
31/**
32 * Explanatory text above the section fields.
33 */
34function sdstudio_visiting_card_section_cb( $args ) {
35 ?>
36 <p id="<?php echo esc_attr( $args['id'] ); ?>">
37 Contact details displayed in the site footer.
38 </p>
39 <?php
40}
41
42/**
43 * Renders a text input field.
44 */
45function sdstudio_footer_phone_field_cb() {
46 $value = get_option( 'footer_phone', '' );
47 printf(
48 '<input type="text" id="footer_phone" name="footer_phone" value="%s" class="regular-text" />',
49 esc_attr( $value )
50 );
51}

After adding the code, go to the admin panel: Settings → General, and below the standard fields you will see the "Business card site settings" section with the "Footer phone number" field. Save the page, and the value will be written to wp_options.

The code is placed in the functions.php of the active theme or, more correctly, in a separate plugin. A plugin survives a theme switch, while functions.php does not. If the section is only needed while a specific theme is active, functions.php is acceptable; in all other cases, only a plugin.

The $args argument: what changed in WordPress 6.1

Before version 6.1, add_settings_section had exactly four parameters, and you had to "touch up" the section wrapper via CSS by ID, or reach into the output buffer. Starting with 6.1, a fifth parameter $args was added, and now the HTML framing is set directly when registering the section:

  • before_section, a div wrapper or an explanatory banner BEFORE the content;
  • after_section, a closing tag or a hint AFTER the content;
  • section_class, a custom class if the standard form-table is not enough.

An example with an explanatory banner and a custom class:

1add_settings_section(
2 'api_keys_section',
3 'API Keys',
4 'sdstudio_api_keys_section_cb',
5 'general',
6 array(
7 'before_section' => '<div class="notice notice-info inline"><p>Store keys in wp-config.php, override here only.</p></div>',
8 'after_section' => '',
9 'section_class' => 'api-keys-section',
10 )
11);

What is important to remember: after_section is not output if the section is empty, meaning when there are no registered fields inside it. If you are counting on a closing </div>, make sure at least one field is added via add_settings_field. And most importantly: use do_settings_sections(), not do_settings_fields(), as the latter will ignore $args entirely.

Before using before_section / after_section, check the WordPress version, on sites older than 6.1 passing the fifth parameter will cause a fatal error. A safe approach: wrap the call in a global $wp_version check or use function_exists to verify the presence of hooks.

⁉️🤔 Frequent questions

Can I skip add_settings_section and add fields directly?

Formally, no. Fields are registered via add_settings_field(), and the fifth parameter of this function requires a section ID. If the section does not exist, the field will not render. For a single field, you can create a section with an empty title and a '__return_false' callback, the section will be invisible, and the field will work.

What is the difference between add_settings_section and add_settings_field?

A section is a container with a title and descriptive text. A field is a specific input element (input, select, checkbox) inside a section. One section can contain any number of fields, all grouped under one heading and saved with a single click of the "Save" button.

What happens if I specify a non-existent page slug in $page?

The section will be registered in $wp_settings_sections but will never render, as WordPress does not know on which page to output it. There will be no error, the section will simply "hang in the air". Check the slug: for built-in pages it is general / reading / writing / discussion / media; for custom ones, the exact slug from add_menu_page or add_options_page.

Can I add a section to another plugin's page?

Yes, if you know its page slug. Pass it in $page, and the section will appear on the other plugin's settings page. But this is a fragile solution: the plugin author can change the slug in an update, and your section will "fall off". Use it only for your own projects or when there are no alternatives.

What does the admin_init hook do and why is add_settings_section called on it?

admin_init fires on every request to the admin panel BEFORE the page is rendered. This is the right moment to register settings: sections and fields must be declared before WordPress starts assembling the form. If you call add_settings_section later, for example, inside the page callback, the section will not make it into $wp_settings_sections and will not be displayed.

Your own section or a separate page, what to choose

Adding a section to general is simpler: less code, and the user sees the settings in the same place where they edit the site title. But when there are more than three or four options, the section bloats the standard page and confuses the administrator.

The rule is simple: one or two options, logically related to the general page (footer phone number, API key for comments), use a section on general. Three or more options, standalone functionality (a slider, CRM integration, a pricing grid), use a separate page via add_options_page(). In both cases, add_settings_section is the very building block where the assembly begins.

Before copying the code, make sure WordPress is updated to the latest version. The $args array, section_class, and before_section/after_section wrappers require at least 6.1, while the function itself has lived in core since version 2.7. Two decades of backward compatibility is not a reason to sit on an outdated engine.