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:
[gist id=”df39e6e3dd5ee177fee30f7e7df548d6″ file=”post-type-metaboxes.php” lines=”7-48″]
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:
[gist id=”df39e6e3dd5ee177fee30f7e7df548d6″ file=”post-type-metaboxes.php” lines=”50-62″]
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):
[gist id=”df39e6e3dd5ee177fee30f7e7df548d6″ file=”post-type-metaboxes.php” lines=”64-87″]
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:
[gist id=”df39e6e3dd5ee177fee30f7e7df548d6″ file=”post-type-metaboxes.php” lines=”89-104″]
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”:
[gist id=”df39e6e3dd5ee177fee30f7e7df548d6″ file=”post-type-metaboxes.php” lines=”106-151″]
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.
Just wanted to say THANK you for posting such a clear, concise tutorial- I’d been reading a number of them, but this one made it all come together for me.
Hey Devin,
Great Post man. Thanks for sharing the code. Great work Devin.
Keep up the good work.
I have to second that comment. So many similar tutorials forget to indicate exactly where to place the code. Thanks again for putting this together.
Nice tutorial.
Thank you for sharing.
let’s tweet this too
Nice tutorial. I will be using it in a few hours. BTW the link to the other article at the bottom appears to be broken. Cheers.
I added a metabox for a custum post type. I added jQuery UI datepicker to an input field inside this meta box.
It works but has a bit of a flaw:
I had to load jQuery UI core from google apis (local WP version wouldn’t work) using enqueue script. Now however, it loads this for the entire WordPress Admin UI and this seems to interfere with the default jquery scripts on WordPress Admin Panel. (the boxes in the sidebar don’t slide open anymore, I have to reload before it works.)
Now, for my question: how can I load the scripts and styles for my datepicker so they only load on my custom post type admin UI?
Is there a function which does something like this?:
if((is_admin)&&(is_mycustomposttype)){
load scripts;
}
Any ideas how to solve this problem?
I had a similar issue while working on an events custom post type: https://wptheming.com/2010/08/how-to-make-an-events-custom-post-type/
I’m guessing something like this would work:
global $post;
if ($post->post_type == ‘events’)
Thanks Eric. I fixed that link.
@Devin Thank you for the reply.
Where would you put this if ($post->post_type == ‘events’) in the above example? I can’t make it work.
I am currently trying out a different path:
add_action(‘admin_print_scripts-(page-hook)’, ‘init_enqueues’);
But the problem with this one is that I don’t know what the page-hook for a custom post type is.
@Peter, you put it in your enqueues function. Here is mine for example..
// Sermon JavaScript
add_action(‘admin_print_scripts’, ‘sermon_js’);
function sermon_js() {
global $post;
if($post->post_type == ‘sermon’) {
wp_enqueue_script(‘hh-jquery-ui’, ‘https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js’, array(‘jquery’));
}
}
Devin- have you seen? http://farinspace.com/wpalchemy-metabox/ It is metaboxes on steroids…. all the hard coding is done for you, and you just define the box, and then create the html for the form inputs.
Good stuff! Using this code I’ve edited your Portfolio Theme to add a “Portfolio Site URL” – allowing me to change the featured image into a link to the website I built.
It might even be worth adding this into your theme for the next release?
Works a charm – Thanks!
Excellent! That was my hope with the post. I wanted to leave the portfolio theme more generalized for people that may not need a metabox for links- but if it is a highly requested feature I’ll add- or provide the option in an options panel.
Hi Devin,
Really great tutorial, thanks for putting this together!
Question for metaboxes. Has anyone tried to recreate the + Add New Category box? For creating a metabox that would just be full of list items say example some html links.
So instead of leaving up to the client to try an use a wysiwyg metabox they would just click add new and paste a link. repeat
Also instead of adding a set amount of metaboxes like 10 or more [ link 1, link 2… ]
It sounds like you’re wanting a custom taxonomy. That’s actually really easy to do: http://codex.wordpress.org/Function_Reference/register_taxonomy. You can set it up to work just like tags or categories- and actually make a lot of sense for what you’re asking.
I want the functionality of the box that creates the list true. But I need the form entries to be outside href links, not in site searches.
Devin, I spent a bit of time trying to track down how to do this and, fortunately, ended up on your site. Thanks a lot for sharing the code in an expertly articulated manner.
how do you add a custom icon to the meta box field, like if i wanted to have a camera type then to have a camera beside it?
You can put any html you wish in the metabox area. Search this page for “// Echo out the field”- right under that you could echo out anything you wanted, including an image.
Thank you Devin for this tutorial, it really helped me out a lot.
I do however have one question. If i want to have a certain meta box on multiple custom post types, do i have to code the same box over and over again but with a different reference everytime?
Let’s say i want to have the “Location” -box on more than just the events post type, how do i repeat the function to work on other post types?
No, you would just have to register the metaboxes for multiple post types. See the $page parameter http://codex.wordpress.org/Function_Reference/add_meta_box.
Thanks a lot for this tutorial!
Great tutorial thanks :) Most tutorials about add_meta_box hardly go over anything but you pretty much went over everything and your code actually works unlike other tutorials I’ve tried.
Thanks!
OMG THANK YOU SO MUCH FOR WRITING THIS TUTORIAL!! I was begining to think that creating a custom meta box was WAY harder than I anticipated because every tutorial I went through was crazy complex and dealing with a LOT of form checks and file handling.. All I needed was a simple concept and I could go from there.. SO THANK YOU!
Thank being said.. I tried adding multiple meta boxes per your little ‘aside’ that you had in this tut. The problem I am having is that I have two meta boxes and can write in them, but only the last meta box saves the info.
I scanned the code to see if i had a typo and didn’t see one. I also looked at your save code and if i am correct, it saves anything that has the events_meta function correct?
I renamed your samples from EVENTS to SCHEDULES and from LOCATION to MONDAY and DATES to TUESDAY
Here is my code:
[EDITED]
Try posting this question in the forum (so we can get more eyes on it) and post the code either there or in paste bin (will make it easier to help you debug).
My prbolem was a wall until I read this, then I smashed it.
Hi, i’m using this function and I’ve a problem: when I add “echo get_post_meta($post->ID, “_location”, true);” won’t work in single post. If I manually add the ID of the custom post, it works fine. If I don’t, nothing happens. Why? I put the “echo get_post_etc” inside and outside of the loop: same results.
Solved: missed “global $post;”
Sweet tutorial, thanks! I was able to finally set up an easy way to add social networking icons to my members page, in a way that’s easy for people to fill out.
Great! Post a link if you ever do a write up on it.
First, THANKS!! This is a great tutorial and finally helped me understand custom post types and how they interact with custom meta boxes. Whew!
I had it all working until I added an additional field in my meta box. Now, everything works correctly *except* I can’t get the data from the meta boxes to display on my site. They save correctly in the post, repopulate etc.
[Code removed]
Any idea why this would be? The data did display correctly when I only had one field in the meta box…
Thanks so much for your help and the great tutorial!
Michelle
Michelle, you’ll need to post the code in something like pastebin.com and link to it. You can also post the question in the WordPress forums and link.
You always need to use the post id as far as I know. If you’re outside the loop, you’ll need to use also include global $post before you can get the meta data.
Thanks Devin! I am using it in the Loop (and it did work when I only had one custom field).
Here’s the code on pastebin –
Functions.php – http://pastebin.com/5qHLDLvK
Single.php – http://pastebin.com/9wGZUs54 (lines 42-51)
I’ll keep playing, maybe morning will bring clarity. :-) Thanks again!
Michelle
Thanks Devin! I’ve got it figured out – I combined what you taught me with WPAlchemy and have it all going strong. :)
Thanks for your excellent tutorial, Devin!
Like a few other readers already commented, I looked at quite a few different tutorials before I found yours. Yours is the only one that really works! And it is clearly written and explained!
Here is a question that is definitely “noob-esque”, but, if I have added multiple metaboxes (a textarea field below the text editor and a text input field on the side) do I then have to create a unique callback and save function for each one?
As I am playing with the code, I found this line and am wondering what the numbers “1” and “2” are referring to:
add_action('save_post', 'wpt_save_events_meta', 1, 2);
Hi Erik.
1) Look at: http://codex.wordpress.org/Function_Reference/add_action. Those number are for $priority (when it executes), and $arguments (2, post_id and post).
2) I think one callback should cover it. Just make sure all the fields are listed so they can be saved.
Thanks. This has been incredibly helpful.
Ppl check this out too … wordpress development..
http://techwithketan.wordpress.com/2011/02/09/adding-metabox-in-post/
Hi, I am using this code for all my metaboxes now.
Beside having textfields, are checkboxes supported?
I am trying to have a checkbox to be checked in order to style some events differently based on the checkbox.
It does show in the metabox, but doesn’t keep the checked status after I check and update the post.
Any help?
Checkboxes are a bit funny. The value is true, right? But you need to make sure the attribute checked=”checked” when you output the html code. You can look at options framework code if you want to see how its done there.
This tutorial was great and really helped out a lot, def appreciate the effort put into this. One question:
I have a dozen custom metabox inputs and they all save correctly and get dumped into custom fields. However, they don’t save into the fields in the custom metabox. So, if you go in to update/edit a field and leave the fields blank that don’t need editing, it deletes those custom fields as their values are null, removing them from the post meta entirely. I’ve tried playing around with the save function, but to no avail. The only way to keep them is to fill out every field every time you need to edit one of the post meta items.
Thoughts?
When you refresh/load/save the page your saved values should be in those fields. Is this working for you?
No, this is the part that fails… It is storing the first two values in their respective fields, but the other ten are blank after save. This is a screenshot of the fields after filling them all out and hitting save:
http://min.us/mvnrjC
This is a screenshot of the custom field area, showing that all the values have been dumped into post meta values:
http://min.us/mvjj0j
I have no idea what’s not happening that the metabox fields come back blank. Oddly, the first time, they saved none. Then I got one to stick, then two, but no more since then.
It sounds like you don’t have those set up quite correct then. Read where it says “Get the location data if its already been entered”, and “Echo out the field”. You should be pulling the saved data, and displaying it out in the field. If it is working for the first two and also on the front end once saved, perhaps you just have a typo.
Thanks for this Devin, a really patient walk-through!
Accomplished exactly what i needed too! Thanks!
Hi. i couldn’t find this question answered above, so i’ll go ahead and ask it. How would i handle adding multiple sets of meta boxes. I can see how to add multiple fields…you’ve done it above. however, what about separate meta boxes. what i have to use all the function code twice, and use different names each time?
thanks in advance
You’d loop over whatever field you were trying to add multiple times, or call a function to output it.
See this project for a good implementation: https://github.com/jaredatch/Custom-Metaboxes-and-Fields-for-WordPress
Amazing tutorial. Thank you! Quick question:
Is it possible for there to be a button that allows the user to add a field? So for example, if you wanted to give them the ability to add multiple links but didn’t want to constrain the amount, but rather just have them add a new field for each link. Does that make sense? I know how to display multiple values of a custom field in the theme, but if there was a way to add fields in the metabox that would make it easier for the user than adding a “link” custom field 5 times.
Thanks!
Also – Having a bit of issues with textareas. I’m looking to create one where the user enters the google maps iframe embed codes, and it tends to work the first time they update (in that it shows it) but it’s having a hard time saving. It strips out everything up to the closing /iframe tag. Any suggestions?
Hey Ryan. I don’t have time to look at the code this moment, but it is a stripslashes issue: http://php.net/manual/en/function.stripslashes.php. You can perhaps add slashes on save and then echo it out with slashes stripped.
Great Tutorial.
Really cleared things up for me, just one question…
I’ve got a meta box set up with four fields, how do i get the title of the fields i.e. ‘dresscode’ to appear alongside what has been inputted. ie.
dresscode: smart casual.
Thanks
You output those as straight html. Look at the code under the headline “An Additional Example” where it says //Echo out the field.
Great tutorial. Definitely helped me create a much needed field in the admin section.
Thank you! You just saved my day.
Btw: since WordPress move on, is there any build in support for this now?
Devin, here’s the code that is supposed to provide the output:
ID, "_location", true); ?>
OK, now where do we paste it? I tried events.php, archive-events.php, location.php, archive-location.php, and I get a 404 every time.
I can output the contents of the Events (Add new Event) edit window with a standard loop in index.php. But what is the purpose of the code you provided? How do we output not just the contents of the edit window, but the Location and Dresscode fields as well? How do we output an Events archive?
Thanks!
I’d like to add a textarea field and size it with rows and columns, but I’m not sure how to do this.
If, say, you were to make
_location
a textarea field and size it, how would you do so?Thanks.
The directions I gave you were for global options. You can also do it on a per post, per page basis by un-checking “Allow comments” in the discussion tab of the post edit screen.
Global options? Well, now I’m just more confused than ever. I’ve read too many confusing tutorials, and absorbed just enough to patch together an operative custom post type. Yours is the closest to an all-in-one solution I’ve found, so I’d like to stick with it.
The only way I’ve managed to output custom post type/metabox content is by adding ‘has_archive’ => true to register_post_type, then using a standard loop in a template named archive-my_template.php. Everything else — custom loops or page templates — is a bust. 404 errors all day long.
And again, I’ve seen at least three different methods to echo metabox content. Using
your method, how would I set the size of a textarea field?
Thanks again.
For 404 errors, try refreshing your permalinks.
The metabox output is simply HTML, so you can change the textarea to whatever you like. Re-read the “Generating the HTML for the Metabox” section. All the HTML is simply echo’d out.
Hi Devin,
I followed your tutorial and it works great. the custom post type and meta boxes are created and they save.
However, when editing normal posts and pages i get the following error when I publish or update:
Warning: Cannot modify header information – headers already sent by (output started at /Library/WebServer/Documents/langley/wp-content/themes/langley/functions.php:87) in /Library/WebServer/Documents/langley/wp-includes/pluggable.php on line 897
The code on that line is:
if ( !wp_verify_nonce( $_POST['eventmeta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
I know that that error message is normally down to whitespace in PHP files but I’ve tried to delete any and I’ve exported into textedit and imported again. Nothing seems to get rid of it!
Any help is much, much appreciated!
Keith
I’m having the same problem, including an Undefined index error:
Notice: Undefined index: product_noncename in /Applications/MAMP/htdocs/test_site/wordpress/wp-content/themes/wookie/functions.php on line 328
Line 328 points to:
if ( !wp_verify_nonce( $_POST[‘product_noncename’], plugin_basename(__FILE__) )) {
Any help on how to fix this, even though it’s just a notice.
I’ve tried to modify this for a date field, but two things aren’t happening: (1) when I go back to edit the date information, it’s not showing up in the meta box, and (2) the date information also isn’t showing up in the HTML when I use this tag: ID, “_date”, true); ?>
So, I’m wondering if it isn’t even saving the date information at all. Here’s my code:
// Registers the new post type and taxonomy
function wpt_event_posttype() {
register_post_type( 'events',
array(
'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' )
),
'public' => true,
'supports' => array( 'title', 'editor', 'thumbnail', 'comments', 'excerpt'),
'capability_type' => 'post',
'rewrite' => array("slug" => "events"), // Permalinks format
'menu_position' => 5,
'register_meta_box_cb' => 'add_events_metaboxes'
));
flush_rewrite_rules( false );
}
add_action( 'init', 'wpt_event_posttype' );
// Add the Events Meta Box
function add_events_metaboxes() {
add_meta_box('wpt_events_date', 'Event Date', 'wpt_events_date', 'events', 'side', 'default');
}
// The Event Date Metabox
function wpt_events_date() {
global $post;
// Noncename needed to verify where the data originated
echo '';
// Get the date data if its already been entered
$date = get_post_meta($post->ID, 'events_date', true);
// Echo out the field
echo '';
// Save the Metabox Data
function wpt_save_events_meta($post_id, $post) {
// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !wp_verify_nonce( $_POST['eventmeta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// OK, we're authenticated: we need to find and save the data
// We'll put it into an array to make it easier to loop though.
$events_meta['events_date'] = $_POST['events_date'];
// Add values of $events_meta as custom fields
foreach ($events_meta as $key => $value) { // Cycle through the $events_meta array!
if( $post->post_type == 'revision' ) return; // Don't store custom data twice
$value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value
update_post_meta($post->ID, $key, $value);
} else { // If the custom field doesn't have a value
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // Delete if blank
}
}
add_action('save_post', 'wpt_save_events_meta', 1, 2); // save the custom fields
}
?>
how to disable drag & drop for the metabox?
Thank you for this absolutely great tutorial!
[…] when adding or editing my graduates so that my custom fields would show up just how I wanted them. Devin Price has some great tips for this. Admin Panel for […]
Awesome tutorial!
Thanks so much!
-d
Just want to say thanks for this great tutorial.
As people have said before you put this so clearly compared to other tutorials.
Great job.
Hey devin i wanna thank you for your easy to catch demo about adding metabox. in your above example you were able to add another field and was also able to save it. my problem is how am i going to save data if i add another field but this time a select/dropdown html field?
pls do reply.. tnx
data1
data2
data3
Great writeup Devin, I’m looking for this code and its working fine on my blog. Thanks for sharing such an awesome tutorial.