SponsoredHosinger Special Offer

WP+ WordPress Plugin Development

Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.

The Settings API

WordPress Plugin Development Lesson 10 of 27 ~6 min read

Overview

The Settings API is WordPress's built-in system for plugin settings pages: you register a setting with a sanitising function, describe its sections and fields, and WordPress handles the form submission, nonce check, saving and error messages for you.

Why not write your own form handler?

A hand-built settings form has to verify a nonce, check capabilities, sanitise every field, save, and redirect back with a message — and most hand-built forms miss at least one of those. The Settings API does all of them, the same way core's own settings screens do.

Step 1: register the setting, section and fields

<?php
function gdc_books_register_settings() {
    register_setting(
        'gdc_books_settings_group',   // Option group: ties the form to this setting.
        'gdc_books_settings',         // Option name in wp_options. One array for all fields.
        array(
            'type'              => 'array',
            'sanitize_callback' => 'gdc_books_sanitize_settings',
            'default'           => array(
                'books_per_page' => 6,
                'show_isbn'      => 1,
                'currency'       => 'USD',
            ),
        )
    );

    add_settings_section(
        'gdc_books_display',                          // Section ID.
        esc_html__( 'Display', 'gdc-books' ),         // Heading.
        'gdc_books_display_section_intro',            // Intro text callback.
        'gdc-books-settings'                          // Page slug from add_submenu_page().
    );

    add_settings_field(
        'books_per_page',
        esc_html__( 'Books per page', 'gdc-books' ),
        'gdc_books_field_number',
        'gdc-books-settings',
        'gdc_books_display',
        array( 'label_for' => 'books_per_page', 'min' => 1, 'max' => 24 )
    );

    add_settings_field(
        'show_isbn',
        esc_html__( 'Show ISBN', 'gdc-books' ),
        'gdc_books_field_checkbox',
        'gdc-books-settings',
        'gdc_books_display',
        array( 'label_for' => 'show_isbn' )
    );
}
add_action( 'admin_init', 'gdc_books_register_settings' );
Tip: Store all your plugin's settings in one array option, as above, rather than one option per field. It is one database row, one query, and one thing to delete on uninstall.

Step 2: field callbacks

<?php
function gdc_books_display_section_intro() {
    echo '<p>' . esc_html__( 'Control how books are listed on the front end.', 'gdc-books' ) . '</p>';
}

function gdc_books_field_number( $args ) {
    $options = get_option( 'gdc_books_settings' );
    $value   = isset( $options[ $args['label_for'] ] ) ? (int) $options[ $args['label_for'] ] : 6;

    printf(
        '<input type="number" id="%1$s" name="gdc_books_settings[%1$s]" value="%2$d" min="%3$d" max="%4$d" class="small-text">',
        esc_attr( $args['label_for'] ),
        $value,
        (int) $args['min'],
        (int) $args['max']
    );
}

function gdc_books_field_checkbox( $args ) {
    $options = get_option( 'gdc_books_settings' );
    $checked = ! empty( $options[ $args['label_for'] ] );

    printf(
        '<input type="checkbox" id="%1$s" name="gdc_books_settings[%1$s]" value="1" %2$s>',
        esc_attr( $args['label_for'] ),
        checked( $checked, true, false )
    );
}

Field names follow the pattern option_name[field], so the browser submits them as one array — exactly the shape you registered.

Step 3: the sanitize callback

<?php
function gdc_books_sanitize_settings( $input ) {
    $clean = array();

    $per_page = isset( $input['books_per_page'] ) ? absint( $input['books_per_page'] ) : 6;
    if ( $per_page < 1 || $per_page > 24 ) {
        add_settings_error(
            'gdc_books_settings',
            'gdc_books_per_page',
            esc_html__( 'Books per page must be between 1 and 24.', 'gdc-books' )
        );
        $per_page = 6;
    }
    $clean['books_per_page'] = $per_page;

    // An unchecked checkbox is simply absent from the submitted data.
    $clean['show_isbn'] = empty( $input['show_isbn'] ) ? 0 : 1;

    $allowed           = array( 'USD', 'EUR', 'INR', 'GBP' );
    $clean['currency'] = in_array( $input['currency'] ?? '', $allowed, true ) ? $input['currency'] : 'USD';

    return $clean;
}

Build a new array from known keys. Never return $input itself — that would save any extra field an attacker added to the form.

Step 4: the page

<?php
function gdc_books_render_settings_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( 'gdc_books_settings_group' ); // Nonce, action and option page fields.
            do_settings_sections( 'gdc-books-settings' );  // Every section and field for this page.
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

The form posts to options.php — core's own handler — which verifies the nonce from settings_fields(), checks the capability, runs your sanitize callback and saves. Messages added with add_settings_error() appear automatically on pages under the Settings menu; on a custom top-level page, call settings_errors() yourself above the form.

Practice

  • Build the settings page with the two fields above, plus a currency <select>.
  • Save 99 as books per page and confirm the error appears and 6 is stored.
  • Read the saved array with get_option( 'gdc_books_settings' ) and use books_per_page as your shortcode's default.
Create a free account to save which lessons you've finished. Save my progress