Theme Development

Reusable Custom Meta Boxes Part 1: Intro and Basic Fields


There are a lot of tutorials on creating custom meta boxes, even just here on Wptuts, and it goes to show that having a good knowledge of these and a system for creating them is crucial to a successful WordPress theme or plugin project. In this series we won’t rehash what meta boxes are but instead we’ll create an easy system for plugging them into whatever your latest project is to keep your work moving consistently.

Create a Meta Box

As usual, the first step is to set up our code for creating a meta box. We’re just creating a library that we can come back to, so we’ll use a really generic name for everything. Some settings can also change whether you’re coming from a theme or a plugin, but the simplest way is to build it into a theme, so that’s what we’ll cover here. All of our code will either be placed into functions.php, or in a file that you include from functions.php in your theme.

// Add the Meta Box
function add_custom_meta_box() {
		'custom_meta_box', // $id
		'Custom Meta Box', // $title 
		'show_custom_meta_box', // $callback
		'post', // $page
		'normal', // $context
		'high'); // $priority
add_action('add_meta_boxes', 'add_custom_meta_box');

This will add a meta box to the Write/Edit Post screen.

  • The $id will be added to the box as an id to make it easy to reference in style sheets and other functions
  • The $title will be displayed in the handle of the box
  • The $callback is the function we’ll use to define the output inside the box
  • $page is used to select which post type the box will be used on
  • We use $context to determine where the box will show up on the page
  • Having $priority set at “high” will put the box as close to the editor as we can and factors in other boxes added by core and plugins.

Create the Field Array

Since we’re building something that we can use over and over again, we won’t be defining the html for every field. Instead, we’ll create an array that holds all of the unique information for each field including a “type”. Then we’ll loop through each field and change the html output according to the type.

// Field Array
$prefix = 'custom_';
$custom_meta_fields = array(
		'label'=> 'Text Input',
		'desc'	=> 'A description for the field.',
		'id'	=> $prefix.'text',
		'type'	=> 'text'
		'label'=> 'Textarea',
		'desc'	=> 'A description for the field.',
		'id'	=> $prefix.'textarea',
		'type'	=> 'textarea'
		'label'=> 'Checkbox Input',
		'desc'	=> 'A description for the field.',
		'id'	=> $prefix.'checkbox',
		'type'	=> 'checkbox'
		'label'=> 'Select Box',
		'desc'	=> 'A description for the field.',
		'id'	=> $prefix.'select',
		'type'	=> 'select',
		'options' => array (
			'one' => array (
				'label' => 'Option One',
				'value'	=> 'one'
			'two' => array (
				'label' => 'Option Two',
				'value'	=> 'two'
			'three' => array (
				'label' => 'Option Three',
				'value'	=> 'three'

It’s important that your meta fields have a unique key so a $prefix is defined to make it simple to attach the same one to each field id. Then we begin an array of arrays where each item defines a new field with a label, description, unique id, and the type of field it is. The select box has additional data to add all of the options for the select box. Using this template, you can add as many of each type of field you want. The order in which you add them to the array is the order in which they will output in the meta box.

Outputting the Fields

Now we are ready to set up our callback function and being outputting the html for each field.

// The Callback
function show_custom_meta_box() {
global $custom_meta_fields, $post;
// Use nonce for verification
echo '<input type="hidden" name="custom_meta_box_nonce" value="'.wp_create_nonce(basename(__FILE__)).'" />';
	// Begin the field table and loop
	echo '<table class="form-table">';
	foreach ($custom_meta_fields as $field) {
		// get value of this field if it exists for this post
		$meta = get_post_meta($post->ID, $field['id'], true);
		// begin a table row with
		echo '<tr>
				<th><label for="'.$field['id'].'">'.$field['label'].'</label></th>
				switch($field['type']) {
					// case items will go here
				} //end switch
		echo '</td></tr>';
	} // end foreach
	echo '</table>'; // end table

This code sets up the callback with a table for the fields and loops through each field in a table row.

  • Echo a hidden nonce field to verify the fields when we save them later
  • Start a table and begin a loop through each field from the $custom_meta_fields array.
  • Get the value of the field if it has been saved for the current post already so that we can output it in the field
  • Begin a table row with two cells: a <th> for the label of the field, and a <td> for the field itself.
  • Then we’ll insert our switch case items.
  • Finally, end the table row, the loop, and the table before closing the function

Case: Text Input

The basic idea here is to test what the field type is and change the output accordingly.

// text
case 'text':
	echo '<input type="text" name="'.$field['id'].'" id="'.$field['id'].'" value="'.$meta.'" size="30" />
		<br /><span class="description">'.$field['desc'].'</span>';

The idea here is pretty simple.

  • If the field type is “text”, echo the html code using that field’s array settings
  • The field id is used for the name which will create the meta field’s unique key, and it’s used for the field id which we want so that we can link our label to the field and also call it in our style sheet later if we want
  • The value will output the $meta variable which is empty if this field hasn’t been saved for this post yet.
  • After the field, we’ll output the description for further explanation of what is expected

Case: Textarea

// textarea
case 'textarea':
	echo '<textarea name="'.$field['id'].'" id="'.$field['id'].'" cols="60" rows="4">'.$meta.'</textarea>
		<br /><span class="description">'.$field['desc'].'</span>';

This follows the same principles as with the text field, except follows the standard of textarea html. The $meta is put between the opening and closing tags so that it outputs any saved text.

Case: Checkbox

// checkbox
case 'checkbox':
	echo '<input type="checkbox" name="'.$field['id'].'" id="'.$field['id'].'" ',$meta ? ' checked="checked"' : '','/>
		<label for="'.$field['id'].'">'.$field['desc'].'</label>';

A check box can be a little tricky because the value of $meta is used to determine whether or not the box should be checked. In our code we use an inline conditional that outputs the “checked” attribute if the $meta value exists, and nothing if it doesn’t. The other difference here is that we use the field’s description in another label so that there’s a large clickable area for the user.

Case: Select Box

// select
case 'select':
	echo '<select name="'.$field['id'].'" id="'.$field['id'].'">';
	foreach ($field['options'] as $option) {
		echo '<option', $meta == $option['value'] ? ' selected="selected"' : '', ' value="'.$option['value'].'">'.$option['label'].'</option>';
	echo '</select><br /><span class="description">'.$field['desc'].'</span>';

The select box is tackled in a completely new way.

  • Open the select field
  • Loop through each of the options that we defined in our array
  • Use an inline conditional to determine if the current option is the one saved for the post and output the “selected” attribute if it is
  • Close the select field and add the description

Save the Data

We’ve set up our basic box with a nice array template for using and reusing multiple types of fields. Now we need to loop through them again, verify them, and save them to the post.

// Save the Data
function save_custom_meta($post_id) {
    global $custom_meta_fields;
	// verify nonce
	if (!wp_verify_nonce($_POST['custom_meta_box_nonce'], basename(__FILE__))) 
		return $post_id;
	// check autosave
		return $post_id;
	// check permissions
	if ('page' == $_POST['post_type']) {
		if (!current_user_can('edit_page', $post_id))
			return $post_id;
		} elseif (!current_user_can('edit_post', $post_id)) {
			return $post_id;
	// loop through fields and save the data
	foreach ($custom_meta_fields as $field) {
		$old = get_post_meta($post_id, $field['id'], true);
		$new = $_POST[$field['id']];
		if ($new && $new != $old) {
			update_post_meta($post_id, $field['id'], $new);
		} elseif ('' == $new && $old) {
			delete_post_meta($post_id, $field['id'], $old);
	} // end foreach
add_action('save_post', 'save_custom_meta');

First we’ll go through a few security checks to see if the fields should be saved; nonce, autosave, and user capabilities are all checked. Then we loop through each field again.

  • Get the field’s value if it has been saved before and store it as $old
  • Get the current value that has been entered and store it as $new
  • If there is a $new value and it isn’t the same as old, update the post meta field with the $new value
  • If the $new value is empty and there is an $old value, delete the post meta field $old value
  • If there are no changes, nothing happens


Your final box should look like the one pictured here:

Custom Meta Box

We have started ourselves a nice little library and template for creating a custom meta box with multiple arrays. Now you can easily drop this into any theme or plugin and use the array to add as many text fields, textareas, single checkboxes, and select boxes as you need. In the continuing parts of this series, we’ll begin working with more advanced fields and even a couple fun ones that involve jQuery UI. You won’t want to miss them!

Related Posts
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    Mastering WordPress Meta Data: Querying Posts and Users by Meta DataMetadata
    So far in this series you've learned how to access WordPress meta data, and work with the arrays in which they are returned. We don't just add custom fields to WordPress posts so we can display this information, but it also so we can sort by it. Now that you know how to retrieve and display meta data, it's time to learn how to customize the WordPress loop to return only posts with specific meta values.Read More…
  • Code
    Mastering WordPress Meta Data: Working With LoopsMetadata
    In the first two parts of this series, we covered what meta data is in WordPress and how to work with the arrays that are typically returned. Now that you've learned to do the detective work necessary to find the structure of an array, it's time to learn to use loops to automate the process of outputting an array. Once you learn this important skill you will never have to write repetitive HTML markup again.Read More…
  • Code
    Mastering WordPress Meta Data: Understanding and Using ArraysMetadata
    In the first part of this series, we covered what WordPress meta data is, how it can be retrieved, and the various data types (such as objects or arrays) in which it can be returned. Now it's time to learn about the different types of arrays. When you write an array manually you know what its structure is an what the name of each index is. But when you are building arrays by querying a database, you are going to need to do some detective work to determine the structure of the data returned and the names of the indexes.Read More…
  • Code
    Creative Coding
    How to Add Custom Fields to AttachmentsHow to add custom fields to attachments 400
    You should be familiar with custom fields in WordPress. We use them on a post or a page to add extra data. In WordPress attachments are also saved as posts, so custom fields are available for them too. Today we'll see how we can add some custom fields so that attachments can carry more information than just the default data.Read More…
  • Code
    Creative Coding
    Posting via the Front End: Advanced SubmissionPreview advanced submission
    Today, we will be continuing with our mini-series on inserting posts via the front end, but in this part we will be exclusively looking into how to manage custom meta fields which we may have within our post type. So, let's get ready and begin!Read More…