Skip to main content

Add WordPress Settings Without a Plugin

In a previous article I covered how to add a settings page to WordPress.  Once a settings page is added to WordPress there are five additional steps to adding custom settings without a plugin. They are “adding sections”, “displaying settings”, “adding defaults”, “adding input fields”,“sanitizing input data” and “registering settings with WordPress”. Just like in the previous article, each step is completed by a combination of built-in functions and custom functions outlined below.

Adding the Setting Section

Each group of settings requires a section to place the form fields in. To create a settings section WordPress provides a built-in function called add_settings_section. We will use that function along with two custom functions to add our section.  The add_settings_section function has 4 required parameters that I have outlined below.

The $section_name is a required parameter used by WordPress as a unique identifier for the section. It should be a string value that is namespaced to prevent conflicts with other code.  For this tutorial we will use the string “ltdi_theme_settings_section“.  The “ltdi” at the beginning of the string is the namespace.

The $section_title is a required parameter used by WordPress as a heading for the settings section. It should be a string value formatted for display to the WordPress user.  For this tutorial we will use the string  “Theme Settings”.

The $callback is a required parameter. The value should be the name of a callable function that outputs content between the section heading and the section fields. It is primarily used to display an introduction to the user. For this tutorial we will use the value ”ltdi_section_introduction”.  Notice that this parameter is also namespaced.

The $option_name is a required parameter used by WordPress as a unique identifier for this group of settings. There are a few built-in option names that include “general”, “reading”, “writing”, “discussion”, and “media”. For this tutorial we will use  ”ltdi_theme_settings” which will group all our settings in one place in the WordPress database.

<?php

  function ltdi_register_settings() {
      add_settings_section(
         'ltdi_theme_settings_section',
         __('Theme Settings', 'ltdi'),
	   'ltdi_section_introduction'
	   'ltdi_theme_settings'
      );
  }
  add_action( 'admin_init', 'ltdi_register_settings' );

  function ltdi_section_description(){
      echo 'This is the Section Introduction'
  }

  1. We create the two custom functions, “ltdi_register_settings” and “ltdi_section_description”.
  2. Within the “ltdi_register_settings” function we place the built-in function “add_settins_section” including all the parameters outlined above.
  3. Within the “ltdi_section_introduction” function we echo out placeholder content.
  4. We then use the “ltdi_section_introduction” function name as the third parameter of the “add_settings_section” function.
  5. Then hook the “ltdi_register_settings” function into the “admin_init” hook.

Displaying the Setting Section

Now that we’ve created the settings section, it can be added to the settings page. In the previous article a custom function called “ltdi_settings_page_callback” was created.  Within that function we displayed the page heading “This is the page content” as seen in the example below.

<?php
  
  function ltdi_settings_page_callback() {
      echo __('<h1>This is the page content</h1>', 'ltdi');
  }

To output the settings section we will modify the previous code by adding three built-in WordPress functions “settings_fields“, “do_settings_sections“ and “submit_button” wrapped in some basic HTML form tags.

The settings_fields function accepts one parameter which matches the $option_name parameter from the add_settings_section function above. It will output all the hidden security fields needed for the settings form.

WordPress settings fields output example

The do_settings_sections function accepts one parameter which also matches the $option_name parameter from the add_settings_section function above. It will eventually output all the form inputs on the page.

The submit_button function has no parameters. It will output the submit button for the form.

<?php

function ltdi_settings_page_callback() {
    echo '<div class="wrap">';
    echo __('<h1>This is the page content</h1>', 'ltdi');
    echo '<form method="post" action="options.php">';

    settings_fields('ltdi_theme_settings');
    do_settings_sections('ltdi_theme_settings');
    submit_button();

    echo '</form>';
    echo '</div>';
}

  1. We modify the “ltdi_settings_page_callback” function from the previous article to include some HTML to wrap the page. 
  2. We add HTML form tags with a method value of “post” and an action value of “options.php”.
  3. Within the form tags we include the three built-in WordPress functions that will output our settings form.
Wordpress display settings section output example

Adding the Setting Defaults

It is best practice to provide default values for every custom setting added to WordPress. To do this we will use the built-in functions get_option and add_option.

<?php
  
function ltdi_register_settings() {

    /* Code for settings section goes here. */

    if ( false == get_option( 'ltdi_theme_settings' ) ) {
        $defaults = [
            'setting_field_one' => '',
            'setting_field_two' => '',
            'setting_field_three' => '',
        ];
        add_option( 'ltdi_theme_settings', $defaults );
    }
}
add_action( 'admin_init', 'ltdi_register_settings' );

  1. We continue editing the “ltdi_register_settings” function, just below the code for adding the settings section.
  2. The first line is a conditional to check the returned value of the function get_option() with the parameter “ltdi_theme_settings”, the unique identifier for the group of settings we are attempting to retrieve.
  3. If no settings are found the get_options() function will return false.
  4. If the return value is equal to false, we create an associative array of default values for each of our settings.
  5. We then use the function add_option(), passing our settings unique identifier and the array of defaults as parameters to add our default settings to the database.

Adding the Setting Input Fields

Each WordPress setting requires an input to change the setting.  WordPress provides a built-in function called add_settings_field to add input fields.  This function has 4 required and 2 optional parameters that I have outlined below.

The $setting_name is a required parameter that is used as a unique identifier for each setting. It should be a string value. For this tutorial we will use the strings “setting_field_one”, “setting_field_two”, and “setting_field_three” from my defaults.

The $setting_title is a required parameter used by WordPress as a label for each field. It should be a string value formatted for display to the WordPress user.  For this tutorial we will use the strings  “Setting Field One”, “Setting Field Two”, and “Setting Field Three”.

The $callback is a required parameter. The value should be the name of a callable function that outputs the form input’s HTML you want to use. In this tutorial I am using a text field, so the callback function is named ”ltdi_get_text_field”. Just like previous custom function names, this function is namespaced.

The $option_name is a required parameter that tells WordPress what group of settings to add the setting to. For this tutorial we will use the string “ltdi_theme_settings” which matches the $option_name parameter from above where we created the settings section.

Although the $section_name parameter is optional, to ensure this field is associated with our section we enter our section’s unique identifier  “ltdi_theme_settings_section” to make the connection.

Although the $args parameter is optional, I strongly encourage its use. It should be in the form of an array.  There are two built-in keys to this array, “label_for” and “class”.  It can also be used to pass additional information to your callback function.

When provided, the “label_for” key is used by WordPress to create HTML label tags around the form label.  The provided value will be used as the “for” attribute in that tag.

When provided, the “class” key is used by WordPress to add a custom CSS Class attribute to the wrapping <tr> HTML tags around the input.

<?php

  function ltdi_register_settings() {
      
    /* Code for settings section adding defaults goes here. */
   
    add_settings_field(
        'setting_field_one',
        __('Setting Field One', 'ltdi'),
        'ltdi_get_text_input',
        'ltdi_theme_settings',
        'ltdi_theme_settings_section',
        [
	        'label_for' => 'setting_field_one',
	        'class' => 'setting_field_one'
        ]	
     );


     /* Repeat above code for each setting field. */

  }
  add_action( 'admin_init', 'ltdi_register_settings' );


  function ltdi_get_text_input($args){
      $settings = \get_option('ltdi_theme_settings');
      $value = !empty($settings[$args['label_for']]) ?: '';

      printf(
          '<input type="text" id="%1$s" name="ltdi_theme_settings[%1$s]" value="%2$s" />',
          $args['label_for'],
          $value
       );
  };


  1. Within the “ltdi_register_settings” function, just after the code for adding the defaults, we include our first use of the “add_setting_field” function including the parameters for our first setting.  
  2. We repeat this function for each setting we are adding, making sure to update the parameters for each setting.
  3. We then proceed to create our new “ltdi_get_text_input” callback function for creating our text input.
  4. The first line of code uses the built-in function “get_option” to attempt to retrieve our settings and assign it back to the $settings variable.
  5. The second line uses a ternary and the “label_for” parameter to see if the setting value exists.
  6. If a value exists, it assigns it back to the $value variable, if not it assigns an empty string.
  7. The function proceeds to output the text field with the assigned value.
Wordpress add settings field example

Registration and Sanitization

We now have the fields displaying on the page, but there is one last function required to make it all work. If you try to add values and save, you will get an error message that says “Error: The ltdi_theme_settings options page is not in the allowed options list”.

WordPress settings registration error

This is because the settings need to be registered for WordPress to recognize them.  To get the settings registered requires one built-in function and one custom function.

The register_setting function has two required parameters and one optional parameter.

The $setting_name is a required parameter that is used as a unique identifier for each setting. It should be a string value. For this tutorial we will use the strings “setting_field_one”, “setting_field_two”, and “setting_field_three” from my defaults.

The first parameter $option_group is a required parameter that tells WordPress what settings group to register. It should be a string value that corresponds to an allowed option key name. For this tutorial we will use the string “ltdi_theme_settings” because it is the unique identifier of our settings.

The second parameter $option_name is a required parameter and the name of an option to sanitize and save. For this tutorial we use the string “ltdi_theme_settings” because it is also the name of the option we want to save.

The third parameter $args is an array of a few parameters. Although it is not required, for security it is recommended that we use at least one of the $args. The suggested parameter is “sanitize_callback”. It is also a string value and is used by WordPress to sanitize the input value before it is saved to the database. For this tutorial we will use “ltdi_setting_sanitization”.

<?php

  function ltdi_register_settings() {
      
      /* Code for settings section, defaults and each setting field goes here. */
   
      register_setting(
          'ltdi_theme_settings',
          'ltdi_theme_settings',
          'ltdi_setting_sanitization'
      );
  }
  add_action( 'admin_init', 'ltdi_register_settings' );


  function ltdi_setting_sanitization($input) {
      $output = [];

      foreach( $input as $key => $value ) {
          if (isset( $input[$key])) {
              $output[$key] = strip_tags( stripslashes( $input[ $key ] ) );
          }
      }

      return apply_filters( 'ltdi_setting_validation', $output, $input );
  }

  1. Within the “ltdi_register_settings” function, just after the code for adding each of the form fields, we include the built-in function “register_setting” along with the required parameters.
  2. We then proceed to create our new “ltdi_setting_sanitization” callback function for sanitizing the input data.
  3. The first line of code creates an array for storing the validated settings.
  4. We then loop through each of the incoming settings to see if it has a value.
  5. If it does, we proceed to strip all HTML tags, PHP tags and properly handle quoted strings.
  6. Then we assign the value to the $output array.
  7. Finally we return the $output providing a filter so additional functions can process the data.
Wordpress saved settings field example

Putting It All Together

Here is the final code all put together.  When this code is placed alongside the code from the previous article you will have a functioning WordPress settings page with three text fields.

<?php

function ltdi_register_settings() {
    
    add_settings_section(
        'ltdi_theme_settings_section',
        __('Theme Settings', 'ltdi'),
        'ltdi_section_introduction'
        'ltdi_theme_settings'
    );

    if ( false == get_option( 'ltdi_theme_settings' ) ) {
        $defaults = [
            'setting_field_one' => '',
            'setting_field_two' => '',
            'setting_field_three' => '',
        ];
        add_option( 'ltdi_theme_settings', $defaults );
    }

    add_settings_field(
        'setting_field_one',
        __('Setting Field One', 'ltdi'),
        'ltdi_get_text_input',
        'ltdi_theme_settings',
        'ltdi_theme_settings_section',
        [
	        'label_for' => 'setting_field_one',
	        'class' => 'setting_field_one'
        ]	
    );

    add_settings_field(
        'setting_field_two',
        __('Setting Field Two', 'ltdi'),
        'ltdi_get_text_input',
        'ltdi_theme_settings',
        'ltdi_theme_settings_section',
        [
            'label_for' => 'setting_field_two',
	        'class' => 'setting_field_two'
        ]
    );

    add_settings_field(
        'setting_field_three',
        __('Setting Field Three', 'ltdi'),
        'ltdi_get_text_input',
        'ltdi_theme_settings',
        'ltdi_theme_settings_section',
        [
	        'label_for' => 'setting_field_three',
	        'class' => 'setting_field_three'
        ]	
    );

    register_setting(
        'ltdi_theme_settings',
        'ltdi_theme_settings',
        'ltdi_setting_sanitization'
    );
}
add_action( 'admin_init', 'ltdi_register_settings' );


function ltdi_section_introduction(){
    echo 'This is the Section Introduction'
}


function ltdi_get_text_input($args){
    $settings = \get_option('ltdi_theme_settings');
    $value = !empty($settings[$args['label_for']]) ?: '';

    printf(
        '<input type="text" id="%1$s" name="ltdi_theme_settings[%1$s]" value="%2$s" />',
        $args['label_for'],
        $value
    );
};


function ltdi_setting_sanitization($input) {
 	$output = [];
    
    foreach( $input as $key => $value ) {

        if (isset( $input[$key])) {
            $output[$key] = strip_tags( stripslashes( $input[ $key ] ) );
        }
    }

    return apply_filters( 'ltdi_setting_validation', $output, $input );
}


function ltdi_settings_page_callback() {
    echo '<div class="wrap">';
    echo __('<h1>This is the page content</h1>', 'ltdi');
    
    echo '<form method="post" action="options.php">';
    settings_fields('ltdi_theme_settings');
    do_settings_sections('ltdi_theme_settings');
    submit_button();
    echo '</form>';
  
    echo '</div>';
}


Get Content Direct to Your Inbox