How to Add Custom Fields to Posts and Pages

Child Theming, Extension Development

Last Updated: Time to Read: 10 minutes

Custom Meta fields are simply form fields added to the post editor, customizer or widget options that allow user input. The values of these fields may be submitted and retrieved in a few different ways.

If you are working on a single site install and not a theme/extension, you might find it easier to work with a plugin such as Simnple Meta or  Advanced Custom Fields. Developers may also use extended frameworks like CMB2 or Meta Box in your themes or extensions for more control over repeater and advanced fields.

See the following:

If you are looking to add custom fields to the WordPress Customizer, go to the Adding Customizer Sections & Controls developer guide.

There is no special way to add meta to the post editor from a Layers child theme or plugin – it is done the traditional WordPress way. However, you may take advantage of the Layers framework for building the form elements themselves, by extending Layers_Form_Elements via the wonderful input() helper function.

There are two methods for housing your custom meta, via using a traditional procedural structure, or via a PHP class. The following tutorial follows a procedural order. From extensisons, you should use the class structure described here to put the actions into your constructor, and the functions into your main plugin file or class.

Create the file

To begin, create a PHP document called meta.php and save it to your child theme or plugin’s includes folder. Here is the starting structure of our file, which contains a comment describing what the file contains.

You will be creating the following functions in that file, then including it in your theme or plugin’s main functions file.

In this example, I’ll demonstrate how you could add a Photo Credit and Source URL field to posts to support a Magazine-style site. If you have created a custom post type, you can apply this to a different post type easily.

Add the Meta Box

Line 3: Setup your function. This should have a unique prefix to add_meta_box to avoid potential conflict with other themes or plugins (i.e. do not use ‘layers_child_’ as your prefix).



Line 6: The

variable allows you to setup an array of post types you want your meta box associated with. The default in Layers is simply ‘post’.  If your plugin is adding a custom post type, you can add that post type’s slug to the array, e.g.
$screens = array('post', 'my_portfolio');

Line 9: Here we  use  add_meta_boxes  to create the panel. The following parameters are set:

Line 10:  The unique ID for this meta box. We will reference this later when building the fields.

Line 11: The display title of the meta box. Please note: if the meta box you are creating is intended for public release (in a theme, plugin, etc.), this string should be internationalized using _()

Line 12: The name of the function that will output the contents of the meta box.

Line 13: The post type where this meta box should be displayed. Since we are using a variable to define these on line 6,  we set this to


Line 14: Specifies the section of the page where the meta box should be placed (normal, advanced, or side).

places it below the editor.

Line 15: Specifies the order within the section where the meta box will be placed (high, core, default, or low).

places it above the Layers Options meta panel, directly below the editor.

Now we need to hook this function onto add_meta_boxes:

If you check your Add New Post screen, you should see the box, though you will have a blank panel or an error until the callback function is created.


Build Your Form

The callback we setup on line 11 of our
function is used to output the meta field’s form elements in our meta box.

Line 5: Create the callback function using the name we already defined

Line 8: Use wp_nonce_field to setup a nonce for security purposes.  There are only two required parameters:

  • Action name: This can be anything, and simply adds context to what the nonce is doing. We are outputting a meta option, so we simply reference
    . This should be unique to any other names you define.
  • Nonce name: This is the name of the nonce hidden form field to be created. Once the form is submitted, you can access the generated nonce via
    . To make it simple we just append our action name to nonce e.g

Line 14-15: Here we set two variables to represent our meta field keys using  get_post_meta, $credit_url and $credit_name, which will correspond to our two fields.

Meta-data is handled with key/value pairs. The key is the name of the meta-data element. The value is the information that will appear in the meta-data list on each individual post that the information is associated with.

retrieves the id of the post being created/viewed/queried
, or the second parameter, is the meta key name. This should be unique and semantic.
is used to return the value

Line 16:  Your form elements go last. For most form elements, you can instantiate the Layers_Form_Elements class and use the input() helper function to generate your form fields. For creating special field types such as a WYSIWYG field, we use core WP functions.

Layers Form Elements

Reference: input()

For our purposes we only need two text fields. You can create text, message, icon/image select, image, file, drop-down, checkboxes and radio buttons using input(), and rich text fields using a special core function. “Fancy” fields such as  color pickers, repeatable fields, etc require additional javascripting explained in separate tutorials.

Line 1-2: We start with a condition that checks to ensure Layers is installed (and thus the class we will be extending exists). This is helpful for avoiding class errors if the user happens to have your extension active but Layers is not installed. Set your

value to
new Layers_Form_Elements

Line 4: (optional) Set your field wrapper. Using a paragraph tag with the

class will render your field full width like the Video URL field. If you do not use a wrapper, the fields will simply sit next to one another:


Line 5: (optional) add a

for elements that don’t take a
argument. Labels should be wrapped in __().

Line 6-16: Setup your input() array. Follow this link for detailed information on each parameter.

Note that we reference our meta key names in each input’s


We then use our output variables to set the

of the fields.  By using
, we ensure data is only returned when it exists:
( isset( $credit_url ) ? $credit_url : '' )

Finally, the

parameter allows us to use the existing framework styles for our fields. This is optional – you can replace our class with your own, if preferred (keep in mind admin CSS must be loaded separately from your front-end CSS). Classes for each field type are referenced in the input() link above.

TinyMCE Fields

One exception for Custom Fields vs Widgets/Customizer controls is the rich-text editor field. In widgets, we can use the input type of
. In the admin, we cannot.  Instead, we use the wp_editor function. In this example, we are still inside our


Line 1: create your save function. Ensure this is unique, i.e.


The first part of the function checks to make sure we really want to save the meta input. These lines are based on Tom McFarlin’s

linked in the References section below. Basically, they make sure that the nonce is valid and this isn’t an autosave, and does it in a very efficient way.

Line 5: The first

value should be set to your nonce name defined earlier, and the second set to your action name.

The second part of the function checks to see if any data has been entered into the the two fields we created. If there is text to save, it uses the update_post_meta() function to save the text to the database. This function has 4 parameters:

$post_id = The unique ID of the post being saved. Use

$meta_key = The unique meta key specifying where the input should be saved. In our example, our keys are
which we defined earlier.
$meta_value = The input to be saved to the database.

Lines 13-17: In the code above, we set the required

and meta key for each
function. However, notice that we didn’t give it the meta value of the  input directly. Instead we used sanitize_text_field function to prepare the input before placing it in the database. This isn’t a tutorial about validation and sanitization and not all input types require sanitization, but if you’re working with user input, you should never be placing unchecked user input into the database. Refer to the References section below to check if your field needs a specific sanitization function here.

Now you just need to hook your save function into save_post and you’re done!

Test your code by creating a new post and filling our your fields, then click Save Draft. Your input should be retained after the editor refreshes. If you enable the Custom Fields panel under Screen Options, you should also see your field keys and values:


Both theme’s and plugins use require_once() to include files, however the way you set the path is different.

From a plugin, add it to your main plugin file:

From a child theme‘s functions.php you use

Now that we’ve successfully saved our meta data, it can be displayed anywhere using
, the same way we did it above to be used internally.  This can be done directly in a template, or inside a function you hook into a Layers action.

  • Line 2-3: Setup some variables to grab the value of our saved photo credit and photo credit link fields. Make sure you define the correct meta key name, and that the third argument is true to ensure the value is returned as a string and not an array.
  • Line 6: Condition check to make sure there is a photo credit before we bother outputting any additional HTML. This helps avoid empty elements
  • Line 7: Here we wrap our data in a .
    span. This framework style will apply the normal post meta style to these elements, and ensure customizer colors that target the
    class also work. This is is only important if your custom meta data will be paired with the existing post meta. You may apply your own containers and styling for elements used in other ways and locations.