Most custom post types in WordPress will need (or could benefit from) a unique set metaboxes for entering information.
For example, a “photography” post type might need fields for “location of photo”, “type of camera”, etc. And an “event” post type would probably need a “location” and an “event date”.
Metaboxes aren’t the easiest to set up- so I’ve written up this tutorial which shows how to add a one line field for “location” to an “event” post type.
Hopefully, you’ll be able to use this guide to add any sort of metaboxes you need.
Set Up the Post Type
If you are unfamiliar with how to set up custom post types, check out Justin Tadlock’s excellent tutorial. For this example, I am going to use a post type called “Event”, which goes in my functions.php file:
/**
* Registers the event post type.
*/
function wpt_event_post_type() {
$labels = array(
'name' => __( 'Events' ),
'singular_name' => __( 'Event' ),
'add_new' => __( 'Add New Event' ),
'add_new_item' => __( 'Add New Event' ),
'edit_item' => __( 'Edit Event' ),
'new_item' => __( 'Add New Event' ),
'view_item' => __( 'View Event' ),
'search_items' => __( 'Search Event' ),
'not_found' => __( 'No events found' ),
'not_found_in_trash' => __( 'No events found in trash' )
);
$supports = array(
'title',
'editor',
'thumbnail',
'comments',
'revisions',
);
$args = array(
'labels' => $labels,
'supports' => $supports,
'public' => true,
'capability_type' => 'post',
'rewrite' => array( 'slug' => 'events' ),
'has_archive' => true,
'menu_position' => 30,
'menu_icon' => 'dashicons-calendar-alt',
'register_meta_box_cb' => 'wpt_add_event_metaboxes',
);
register_post_type( 'events', $args );
}
add_action( 'init', 'wpt_event_post_type' );
You may have your own custom post type set up completely different, but that’s fine. The important line of code for the metaboxes is ‘register_meta_box_cb’ => ‘add_events_metaboxes’- which calls the function to build the metaboxes.
You can rename the function to whatever you like, for instance ‘register_meta_box_cb’ => ‘add_photography_metaboxes’ might be better for a photography post type.
If the post type is being registered through a plugin or is one of the native post types, you can also use:
add_action( 'add_meta_boxes', 'add_events_metaboxes' );
Add Meta Box
The following code adds a metabox to the right side of the screen under the “Publish” box:
/**
* Adds a metabox to the right side of the screen under the “Publish” box
*/
function wpt_add_event_metaboxes() {
add_meta_box(
'wpt_events_location',
'Event Location',
'wpt_events_location',
'events',
'side',
'default'
);
}
You can read the full parameters for add_meta_box in the codex. I also listed them here:
add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args );
For the example above:
- $id is “wpt_events_location”- or the html id that will be applied to this metabox.
- $title is “Event Location”. This appears at the top of the new metabox when displayed.
- $callback is the function “wpt_events_location” which will load the html into the metabox.
- $page is “events”, the name of our custom post type.
- $context is “side”. If you wanted it to load below the content area, you could put “normal”.
- $priority controls where the metabox will display in relation to the other metaboxes. You can put “high”, “low” or “default”.
If you wanted to have two sets of metaboxes, perhaps one on the side and one below the content area, you could do something like this (Note: Don’t use this if you’re following the tutorial step by step, this is just an example of how it would be done):
/**
* If you wanted to have two sets of metaboxes.
*/
function add_events_metaboxes_v2() {
add_meta_box(
'wpt_events_date',
'Event Date',
'wpt_events_date',
'events',
'side',
'default'
);
add_meta_box(
'wpt_events_location',
'Event Location',
'wpt_events_location',
'events',
'normal',
'high'
);
}
You’d then have to make sure the two function wpt_events_date and wpt_events_location were defined to call the html code to go inside the metaboxes.
Generating the HTML for the Metabox
Continuing with the first example above, we’ll now have to generate the code that goes inside our “Event Location” metabox. To keep this as simple as possible, we’re just going to make one field:
/**
* Output the HTML for the metabox.
*/
function wpt_events_location() {
global $post;
// Nonce field to validate form request came from current site
wp_nonce_field( basename( __FILE__ ), 'event_fields' );
// Get the location data if it's already been entered
$location = get_post_meta( $post->ID, 'location', true );
// Output the field
echo '<input type="text" name="location" value="' . esc_textarea( $location ) . '" class="widefat">';
}
At this point you should have a metabox showing up in your post. If you check your “events” post type, it should load on the right side like in the screenshot I posted. This will generate any html you choose, so, you could put as many input fields in here as you like, or html descriptions.
In order to class the inputs and descriptions correctly, check out the source code for other write panels in WordPress. See how they do textareas and select boxes. You can even add icons and generated text in these spots.
Saving
If you had tried to save your metabox data before this point, it just would have disappeared on the refresh because it wasn’t being saved. Here’s the code that updates the metabox when you click “Update”:
/**
* Save the metabox data
*/
function wpt_save_events_meta( $post_id, $post ) {
// Return if the user doesn't have edit permissions.
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
// Verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times.
if ( ! isset( $_POST['location'] ) || ! wp_verify_nonce( $_POST['event_fields'], basename(__FILE__) ) ) {
return $post_id;
}
// Now that we're authenticated, time to save the data.
// This sanitizes the data from the field and saves it into an array $events_meta.
$events_meta['location'] = esc_textarea( $_POST['location'] );
// Cycle through the $events_meta array.
// Note, in this example we just have one item, but this is helpful if you have multiple.
foreach ( $events_meta as $key => $value ) :
// Don't store custom data twice
if ( 'revision' === $post->post_type ) {
return;
}
if ( get_post_meta( $post_id, $key, false ) ) {
// If the custom field already has a value, update it.
update_post_meta( $post_id, $key, $value );
} else {
// If the custom field doesn't have a value, add it.
add_post_meta( $post_id, $key, $value);
}
if ( ! $value ) {
// Delete the meta key if there's no value
delete_post_meta( $post_id, $key );
}
endforeach;
}
add_action( 'save_post', 'wpt_save_events_meta', 1, 2 );
This code checks to make sure the user has privileges to update the post, then saves the data that’s in the event_location field.
Other Resources
If you need to add a lot of custom meta fields (especially more complex ones like date pickers, file uploaders, etc) you may want to consider using a library like CMB2 or Advanced Custom Fields.
I also created a boilerplate plugin for a team post type with metaboxes if you’d like to view the code for that.
All the code for this post is here. Please share and enjoy.