Creating meta boxes is a crucial part of WordPress
theme/plugin development. It's a way to add an appealing editor to the
post screen and avoids forcing users to rely on custom fields. If you've
ever created a custom post type in WordPress, you've probably wanted to
add some sort of additional data to it. Sure, you could use custom fields, but that's ugly. Getting started with custom meta boxes is easy, so let's dive in!
A custom meta (or write) box is incredibly simple in theory. It allows you to add a custom piece of data to a post or page in WordPress - what's better is that it can fit directly into most of the default pages inside WP, so you can easily place it inside the Post-Editor for easy use by non-technical types. As I said in the intro, you can add this same kind of "meta data" to your post using the built in custom fields for a post or page. There's nothing wrong with this persay, but it's not a very graceful or user-friendly.
Instead, you want to create a custom meta box that contains fields for all of your data and saves all of that stuff right when the post is published. That's what we're covering here. This is broken down into three big steps:
For the advanced readers in the audience: Yes, custom post types is where we'll be going with this eventually, but it's important to setup some fundamentals first. Plus, as you can use the custom meta boxes in all sorts of places, it's good for anyone to know.
Anything in this tutorial will work in a theme's
Conveniently, WordPress provides a function for adding meta boxes to a given admin screen: add_meta_box.
The codex entry is well done for this function, but here's a brief overview. Its prototype:
Finally
So, our
We can't just pop this into our plugin file alone. Doing so will
result in the white screen of death, and PHP fatal error: call to
undefined function. Why? Because we called
Our code to add the meta box to our post screen would look like this:
Remember the string we passed as the
We're going to add several fields to our meta box: a text input, a
drop down menu, and a check-box. Let's start with the text input.
But what about actually displaying the data? Well, as you'll see in
step 3, we'll store this data in the wp_postmeta table using the
update_post_meta function. That function has two sister functions
called get_post_meta and get_post_custom, which grab data from wp_postmeta.
Also note that the
With the addition of a second field, we changed or
In the drop down, we're going to use one of WordPress's most handy functions: selected. This compares the first value, the data we saved, with the second, the
Again WordPress provides the handy function checked(). It works just like
The
Now the fun stuff: actually saving our data. The number one rule when putting anything into your database or on your site is don't trust the user.
Even if that user is you. To that end, before we save any data, we
want to make sure there's nothing malicious in there. Fortunately
WordPress provides a bunch of functions for data validation.
You already saw
We're going to use the
To use these custom fields on the front end of your site, use the
What Are Custom Meta Boxes?
A custom meta (or write) box is incredibly simple in theory. It allows you to add a custom piece of data to a post or page in WordPress.Imagine that you're working on a theme for a client that wants to catalog his extensive collection of concert posters. You immediately start looking to the core WordPress functionality to see how you might organize the theme: Every post will represent a poster, which is perfect for adding an image, title and description. We can also use the categories and tags system inside of WordPress to organize the posters. But what if we wanted to add a new type of meta data for the "artist" of each poster? Hmph. WordPress doesn't quite have anything for that right out of the box... which brings us to custom meta boxes.
A custom meta (or write) box is incredibly simple in theory. It allows you to add a custom piece of data to a post or page in WordPress - what's better is that it can fit directly into most of the default pages inside WP, so you can easily place it inside the Post-Editor for easy use by non-technical types. As I said in the intro, you can add this same kind of "meta data" to your post using the built in custom fields for a post or page. There's nothing wrong with this persay, but it's not a very graceful or user-friendly.
Instead, you want to create a custom meta box that contains fields for all of your data and saves all of that stuff right when the post is published. That's what we're covering here. This is broken down into three big steps:
- Adding the meta box
- Rendering the meta box
- Saving the data (the right way - yes, there is a wrong way)
For the advanced readers in the audience: Yes, custom post types is where we'll be going with this eventually, but it's important to setup some fundamentals first. Plus, as you can use the custom meta boxes in all sorts of places, it's good for anyone to know.
Anything in this tutorial will work in a theme's
functions.php
file. That is not the correct place for it, however. If you're adding
data to a post, chances are you want it there regardless of your front
end design. As such, you should place this code some place that isn't
dependent on your design: a plugin file.Step 1 Adding the Meta Box

The codex entry is well done for this function, but here's a brief overview. Its prototype:
1
| <?php add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args ); ?> |
$id is the html ID attribute of the box. This is useful
if you're loading custom CSS or Javascript on the editing page to
handle the options. Otherwise, it doesn't really matter all that much.$title is displayed at the top of the meta box.$callback is the function that actually renders the meta box. We'll outline this in step 2.$page is where you want the meta box to be displayed. This should be a string with 'post' or 'page' or 'some_custom_post_type'.$context is where you want the meta box displayed.
'normal' puts it below the post editor. 'side' moves the meta box to
editing screen's right sidebar (by the categories and tags, etc).
'advanced' also put the box in the same column as the post editor, but
further down.$priority tells wordpress where to place the meta box in
the context. 'high', 'default' or 'low' puts the box closer to the
top, in its normal position, or towards the bottom respectively. Since
all meta boxes are drag-able, $priority is not a huge deal.Finally
$callback_args lets you pass data to your $callback
function in the form of an array. We're not going to use this here,
but it could be useful for passing some data to the meta box. Say, if
your plugin had several options that influenced what was displayed in
the meta box. You could pass those options values through the $callback_args array.So, our
add_meta_box call will look like this:
1
| <?php add_meta_box( 'my-meta-box-id', 'My First Meta Box', 'cd_meta_box_cb', 'post', 'normal', 'high' ); ?> |
add_meta_box function before WordPress was loaded. So we need to use a WordPress hook, which is part of the plugin api.
Basically, functions get hooked into a given WordPress action or
filter hook, then those functions are fired when that hook loads. By
wrapping our add_meta_box call in a function, then hooking that function
into the add_meta_boxes action hook, we avoid the fatal error.Our code to add the meta box to our post screen would look like this:
1
2
3
4
5
6
7
| <?phpadd_action( 'add_meta_boxes', 'cd_meta_box_add' );function cd_meta_box_add(){ add_meta_box( 'my-meta-box-id', 'My First Meta Box', 'cd_meta_box_cb', 'post', 'normal', 'high' );}?> |
Step 2 Rendering the Meta Box
The above code is enough to add the meta box, but now we have to render the thing and actually add fields. This is just an HTML form code mixed in with a bit of PHP to display the saved data. We don't need to include the form tags as WordPress does that for us.Remember the string we passed as the
$callback in add_meta_box? We're now going to create a function with the same name. This function will take care of all the display inside the meta box.
1
2
3
4
5
6
| <?phpfunction cd_meta_box_cb(){ echo 'What you put here, show\'s up in the meta box'; }?> |

Adding the Text Input
1
2
3
4
5
6
7
8
9
| <?phpfunction cd_meta_box_cb(){ ?> <label for="my_meta_box_text">Text Label</label> <input type="text" name="my_meta_box_text" id="my_meta_box_text" /> <?php }?> |

get_post_meta only grabs data from one key, while get_post_custom grabs all of it. Because we're only really using one field at this point, let's use get_post_meta.Also note that the
add_meta_box function passes one variable to our callback: $post, which is a post object.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
| <?phpfunction cd_meta_box_cb( $post ){$values = get_post_custom( $post->ID );$text = isset( $values['my_meta_box_text'] ) ? esc_attr( $values['my_meta_box_text'][0] ) : ”;$selected = isset( $values['my_meta_box_select'] ) ? esc_attr( $values['my_meta_box_select'][0] ) : ”;$check = isset( $values['my_meta_box_check'] ) ? esc_attr( $values['my_meta_box_check'][0] ) : ”; ?><p> <label for="my_meta_box_text">Text Label</label> <input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?>" /> </p> <?php }?> |
Adding The Drop Down
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <?php function cd_meta_box_cb( $post ){$values = get_post_custom( $post->ID );$text = isset( $values['my_meta_box_text'] ) ? esc_attr( $values['my_meta_box_text'][0] ) : ”;$selected = isset( $values['my_meta_box_select'] ) ? esc_attr( $values['my_meta_box_select'][0] ) : ”;$check = isset( $values['my_meta_box_check'] ) ? esc_attr( $values['my_meta_box_check'][0] ) : ”; ?> <p> <label for="my_meta_box_text">Text Label</label> <input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?>" /> </p> <p> <label for="my_meta_box_select">Color</label> <select name="my_meta_box_select" id="my_meta_box_select"> <option value="red" <?php selected( $selected, 'red' ); ?>>Red</option> <option value="blue" <?php selected( $selected, 'blue' ); ?>>Blue</option> </select> </p> <?php }?> |
get_post_meta call to get_post_custom,
which returns an associative array of all the post's custom keys and
values. We then just access our fields via their names. The ternary
statements keep our code from throwing PHP warnings (undefined indices
and such). We'll cover the esc_attr function in step three.In the drop down, we're going to use one of WordPress's most handy functions: selected. This compares the first value, the data we saved, with the second, the
<option>'s value attribute. If they're the same, the function will echo selected="selected",
which makes that value display on the drop down. Pretty sweet, and it
saves us from writing a bunch of if or ternary statements. You can also
use the selected() function with radio buttons.Adding the Check-box
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| <?phpfunction cd_meta_box_cb(){ // $post is already set, and contains an object: the WordPress post global $post; $values = get_post_custom( $post->ID ); $text = isset( $values['my_meta_box_text'] ) ? $values['my_meta_box_text'] : ''; $selected = isset( $values['my_meta_box_select'] ) ? esc_attr( $values['my_meta_box_select'] ) : ''; $check = isset( $values['my_meta_box_check'] ) ? esc_attr( $values['my_meta_box_check'] ) : ''; // We'll use this nonce field later on when saving. wp_nonce_field( 'my_meta_box_nonce', 'meta_box_nonce' ); ?> <p> <label for="my_meta_box_text">Text Label</label> <input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?>" /> </p> <p> <label for="my_meta_box_select">Color</label> <select name="my_meta_box_select" id="my_meta_box_select"> <option value="red" <?php selected( $selected, 'red' ); ?>>Red</option> <option value="blue" <?php selected( $selected, 'blue' ); ?>>Blue</option> </select> </p> <p> <input type="checkbox" id="my_meta_box_check" name="my_meta_box_check" <?php checked( $check, 'on' ); ?> /> <label for="my_meta_box_check">Do not check this</label> </p> <?php }?> |
selected() comparing the first value (our saved data) to the second and echoing out checked="checked" if they're the same.wp_nonce_field adds two hidden fields to our meta box. One of them is a nonce.
These are random strings of numbers that are valid on per user per
blog basis for 24 hours. Nonces are a way of verifying intention, and
they make sure that WordPress doesn't do anything unless the request
came from a very specific place. In other words, we don't want to
accidentally update our data by somehow running our save function (see
step 3) in another location other than the save_post hook, so we check
to make sure the nonce is valid before doing anything.
Step 3 Saving the Data
The number one rule when putting anything into your database or on your site is don't trust the user. Even if that user is you.To save our data, we're going to rely on another WordPress hook:
save_post. This works just like our action hook above:
1
| <?php add_action( 'save_post', 'cd_meta_box_save' ); ?> |
cd_meta_box_save function will receive one argument, the post id, and take care of cleaning and saving all of our data. The save_post hook fires after the update or save draft button has been hit. So we have access to all the $_POST
data, which includes our meta box fields, within our saving function.
Before we can do anything, however, we have to do three things: check if
the post is auto saving, verify the nonce value we created earlier, and
check to make sure the current user can actually edit the post.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| <?phpadd_action( 'save_post', 'cd_meta_box_save' );function cd_meta_box_save( $post_id ){ // Bail if we're doing an auto save if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; // if our nonce isn't there, or we can't verify it, bail if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return; // if our current user can't edit this post, bail if( !current_user_can( 'edit_post' ) ) return;}?> |
You already saw
esc_attr() above (step 2). This one
encodes ' " and < > into HTML entities. Why use this? So users
couldn't type a <script> into your meta box. If you'd like to
allow certain HTML tags in, but strip others, wp_kses can
do that. It takes two arguments, the first of which is the string you'd
like to check and the second is an associative array of allowed tags.
WordPress provides many more data validation tools, just don't be afraid
to use them.We're going to use the
update_post_meta function to date care of saving our data. It takes three arguments: a post ID, the meta key, and the value.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| <?phpadd_action( 'save_post', 'cd_meta_box_save' );function cd_meta_box_save( $post_id ){ // Bail if we're doing an auto save if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; // if our nonce isn't there, or we can't verify it, bail if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return; // if our current user can't edit this post, bail if( !current_user_can( 'edit_post' ) ) return; // now we can actually save the data $allowed = array( 'a' => array( // on allow a tags 'href' => array() // and those anchors can only have href attribute ) ); // Make sure your data is set before trying to save it if( isset( $_POST['my_meta_box_text'] ) ) update_post_meta( $post_id, 'my_meta_box_text', wp_kses( $_POST['my_meta_box_text'], $allowed ) ); if( isset( $_POST['my_meta_box_select'] ) ) update_post_meta( $post_id, 'my_meta_box_select', esc_attr( $_POST['my_meta_box_select'] ) ); // This is purely my personal preference for saving check-boxes $chk = isset( $_POST['my_meta_box_check'] ) && $_POST['my_meta_box_select'] ? 'on' : 'off'; update_post_meta( $post_id, 'my_meta_box_check', $chk );}?> |

Wrap Up
That's it! You should have a fully working meta box. Other examples you may find around the web loop through a bunch of fields without really cleaning the data. This is the wrong approach. Always use the built in data validation functions; different fields/values may require different data validation.To use these custom fields on the front end of your site, use the
get_post_meta or get_post_custom functions (see step 2).
Post a Comment
Post a Comment