Written by Bill EricksonPublished on March 31, 2011Updated on December 21, 2018
Overview
What is metadata?
Metadata is information about an object (like a post) and consists of a key and a value. For instance, when you use a page template, the name of the template used by the page is stored as metadata using the key _wp_page_template. You can add your own metadata to store anything you’d like.
This article focuses on post meta, which is metadata attached to a post of any content type (post, page, or custom post type). There is also user meta (attached to users) and term meta (attached to taxonomy terms, like categories and tags).
What is a metabox? A metabox is simply a box that lets you edit metadata. While WordPress already includes a simple “Custom Fields” metabox (screenshot), it’s best to build your own metabox, which will look better and be easier to use.
A common use for metaboxes is to store structured data that is then displayed by the theme. This allows you to separate the data entry from visual presentation. Instead of trying to get the markup just right on a pricing table, create a “Pricing Table” metabox to collect the fields and let the theme take care of the markup.
How to use meta in your theme? WordPress has a simple function for accessing post meta: get_post_meta( $post_id, $key, true );. See the WordPress Code Reference for more information on its usage.
Depending on the plugin you use and the complexity of the fields you build, you may choose to use plugin-specific functions to access metadata. For instance, with Carbon Fields you can use carbon_get_post_meta( $post_id, $key );. Always first check that the function exists( if( function_exists( 'carbon_get_post_meta' ) ) ) so your site doesn’t break if the plugin is deactivated.
Creating Metaboxes with Plugins
The simplest way to create a metabox is to use a “metabox plugin” to do the heavy lifting. You describe the fields you want and the plugin takes care of actually building the metabox and saving the data.
If you will be publicly distributing a plugin, it’s best to build the metabox directly in the plugin itself rather than require a second plugin to be installed. Here are two examples of plugins that use custom metaboxes:
I’ll walk you through building the metabox for this page using three popular metabox plugins.
Advanced Custom Fields
Advanced Custom Fields is the most popular plugin for creating metaboxes. You can easily build a metabox from the WordPress backend with no code at all. You can use the Local JSON feature to save your metabox settings in your theme so it can be version controlled. While the free version of the plugin includes many basic field types, this tutorial uses the “Repeater” field which is only available in ACF Pro.
In the backend of your site, go to Custom Fields > Field Groups and click “Add New”. Give your metabox a name at the top, then click “Add Field” to add your fields. Here is a list of the available field types.
I’m adding a repeater field, and inside of that I’m adding some text, textarea, and checkbox fields. I’m also setting the “width” on the first 3 fields to 33% so they appear in three columns.
In the “Location” section you can set rules that determine where this metabox appears. I’m limiting this metabox to appear on pages using the “Plugins (ACF)” template.
To access the data in your theme, you can either use the WordPress core function get_post_meta() or the ACF functions. I’m using the ACF functions in the template file below. Here’s the raw metadata stored in the database.
To keep the example simple and easily comparable between plugins, I’m putting all the data into an array and passing it to a function that builds the plugin listing.a
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Carbon Fields is another great metabox tool. You build metaboxes using code rather than a UI like AC. The code is modern and easy to read, and the resulting metaboxes look great. It takes the best from both ACF and CMB2 and improves upon it. They are also working on a block builder for the new block editor.
Installing the plugin is more difficult than the others. While there is a Carbon Fields plugin in the WordPress plugin repository, it’s not up-to-date. They made non-backwards-compatible changes and decided not to update the plugin repo. You can either use Composer or download the plugin here.
Once installed and active, create carbon-fields.php file in your core functionality plugin (preferred) or theme. See the documentation for more information on how to build a metabox.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I have it set to ->set_collapsed( true ) so when you load the page you only see the small header bars (like “Genesis Title Toggle” below), but in this screenshot I’ve expanded the second entry.
I’m using the plugin functions for accessing the metadata. In case you’re curious, here’s the raw metadata that’s saved to the database. It isn’t as easy to follow as ACF or CMB2 so I definitely recommend using the plugin functions. While it is all stored as separate meta, on “complex” fields carbon_get_post_meta() will return a single array containing all the data.
Here’s the template file:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CMB2 is the most developer focused of these three plugins. It’s infinitely extensible and often used in large projects since every aspect can be customized, including custom styles for the metabox. But it can be a bit difficult to learn and doesn’t look as good out of the box as the other plugins I’ve covered.
You can either use the WordPress plugin (as I have done) or include the CMB2 code directly in your theme/plugin. It’s smart enough to only load once when multiple copies are enqueued, and always select the most recent version.
In my core functionality plugin I created cmb2.php and added the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CMB2 uses the standard WordPress function get_post_meta() for retrieving meta, so there are no plugin-specific functions you need to learn.
Because we are using the “Group” field type for repeatable fields, it stores all of that data in a single post_meta field as a serialized array (see the raw metadata here), which WordPress unserializes into a standard array. Here’s the page template code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
A hybrid WordPress theme uses theme.json to define styles and customize the block editor while also using traditional PHP template files. Hybrid themes leverage the block editor for content but…
I frequently work with food bloggers to improve the design, speed, and functionality of their websites. The recipe card is the most important element on the page. It should be…
A detailed guide to importing recipes from *any* theme or plugin to WP Recipe Maker.
Reader Interactions
Comments are closed. Continue the conversation with me on Twitter: @billerickson
Comments
Bill Ericksonsays
@ Charlie, it’s a recommended coding practice in WordPress to prefix everything. If you install a plugin or theme that uses the custom field “project_text” you could have some bad interactions. By creating your own prefix you minimize the chances of using the same field name (what’s the chance someone created a field called be_project_text or crm_project_text).
Artin H.says
Hey Bill,
First of all thank you for this great plugin/code.
I just installed it on one of my themes and it works great.
I do have one question.
I created a field (‘type’ => ‘textarea’) and I need to add some HTML code in the textarea and I need the html code to do something in the front end. However, the code gets displayed in text-format.
Example:
I have the following ul list
item 1
item 2
item 3
I need this to be displayed as an unordered list in the front-end. But what I get is the code displayed in text-format.
I know I have the option of changing the field type to ‘wysiwyg’, but I need to create several custom fields in “page” view and I would prefer if they were ‘textarea’ instead of ‘wysiwyg’.
Please let me know if this is possible and if yes, how would I go about doing it.
Thank you in advance.
Best,
Artin H.
Bill Ericksonsays
When rendering it on the frontend, you’ll have to use the html_entity_decode() function. Ex:
$embed = get_post_meta($post->ID, ’embed’, true);
echo html_entity_decode( $embed );
Artin H.says
Thank you Brill.
Everything looks great.
Artin H.says
Hey Bill,
I’ve been using your code and it’s working great. Thank you very much.
I do have one question. In one of my themes, I have added a ‘Radio button’ with two values: Yes/No
Instead of using a radio with Yes or No, I’d rather use a checkbox. Then you could just check to see if the custom field has a value.
Artin H.says
Great, working perfect.
Also, thanks for the “checkbox” suggestion, that makes more sense.
Charlie Chauvinsays
Bill,
Just curious, I’ve been really enjoying the Custom Meta Boxes, but noticed that the ‘wysiwyg’ option is a little more limiting than the WordPress default where you can insert an image within the text and/or toggle the HTML view. I understand why you would do this, but is there a meta box option that could would include these like the wordpress?
Thanks.
Bill Ericksonsays
I don’t think it’s possible to duplicate the WordPress post editor with the media library. Even if you could add the media library to the alternate WYSIWYG, it would require some serious customizations to the media library to make the image not automatically inserted into the main post editor instead of your alternate one.
If you need the full WordPress editor I’d recommend finding a way to use just the post editor instead of an additional WYSIWYG. For instance, if you’re trying to do multiple content columns, use a content filter or shortcode.
trishsays
thanks so much for this! I love how elegantly it works. I have a question though. I’ve created two custom fields, one for first name & one for last name. I’ve also hidden the post title from the editor. Now I would like to combine the first name & last name fields to give me the post title, such that it would replace WP’s post title field. (Right now, the way I have it set with the post title field hidden in the editor, I am getting ‘Auto Draft’ in the listings in the admin.) I think I need to set up another field that combines both the first name & last name but I’m unsure how that would be set up in the function with the arrays or how it would insert into the Post Title field. Does that make sense? Thanks again!
It’s beyond the scope of this post, but there should be a way to hook into the post title when the post is saved (hook = save_post) and use your post meta like this:
$title = $first .’ ‘. $last;
trishsays
Thanks so much for pointing me to the right direction. For any of your readers who may be interested in the solution, this is what worked for me:
I really appreciate your help and the custom metaboxes!!
Steven Querrysays
Hello Trish,
I would love to be able to accomplish this as well. However I’m new to all of this. I have 5 meta boxes I would like to put together to make the title. I think I’m following along till you get to the first $fname. Could you provide further? I tried this with no luck to start:
In my create metaboxes action I have a prefix of “sq_” – does that need to be included in the above script? Also where does this add_filter go? Does it need to be nested with my create_metaboxes or create_my_post_types?
Thank you so much for you help…
Bill Ericksonsays
I’m sorry, I don’t have any experience editing the title on save so I can’t help. But why edit it there? Allow the user to construct the title however they want and then on the frontend add the vehicle year and color before displaying the title.
trishsays
Hi Bill,
I have another question regarding the image file upload field. I don’t know if I’m doing something wrong but it doesn’t seem to be working. I’ve got the other fields set up correctly and are being entered and saved properly. I didn’t do anything but copy and paste the code from example-functions file. The field itself is showing up in the editor; the issue is that when I click on ‘Upload File’ button, nothing happens. Am I missing something? I’m working off my localhost so I don’t know if that has something to do with it.
Thanks again!
Trish
Bill Ericksonsays
I’m not sure if the image upload one is working yet. I remember Jared Atchison looking into it. You can try contacting him: [email protected]
Vayu Robinssays
Hi.
Trish did you mail Jared about the file upload function?
Bill, do you know if this function is working yet?
Vayu
Bill Ericksonsays
I’m out of the country so haven’t been keeping up with the CMB development, but my understanding is that it isn’t working at the moment. What I would use is the default media uploader, then once uploaded copy the image url and paste it in a text field.
Mikesays
Well I think I found one senario:
If you name your theme like “My Theme”, it is not posible to access the jquery.cmbScripts.js file and you will get a 403 message.
Name your theme always without spaces, remove the space or use underscore: “MyTheme” or “My_Theme”, because it’s space sensitive!
Good Luck 😉
Brucesays
Trish,
Did you ever find a solution to the image file upload field?
I have the exact same issue. I too am running on localhost (XAMPP).
I have discovered in Firebug that there is a 403 access error on the file – jquery.cmbScripts.js?ver=3.1.3.
I have emailed Jared and am trading emails with him now on it
Thanks
Paul de Wouterssays
Bill
what if I want one set of fields for a CPT, and another set of fields for pages?
Danny G Smithsays
Anyway, I need to have multiple uploads, and it looks like the are being stored as a two dimensional array in the meta fields so it should be possible, but I am not sure how to get it to work in the front-end. is there something that is intuitive that I am missing? The way I am doing it is uploading the file first using the upload box, coping the url, pasting it in the box again, and then it is there below with links to remove, etc. If I try to duplicate the steps again, it appears to replace the first one with the current. By the way, I am going to post this on Bill’s blog as well, in case you see it there first.
Bill Erickson says
@ Charlie, it’s a recommended coding practice in WordPress to prefix everything. If you install a plugin or theme that uses the custom field “project_text” you could have some bad interactions. By creating your own prefix you minimize the chances of using the same field name (what’s the chance someone created a field called be_project_text or crm_project_text).
Artin H. says
Hey Bill,
First of all thank you for this great plugin/code.
I just installed it on one of my themes and it works great.
I do have one question.
I created a field (‘type’ => ‘textarea’) and I need to add some HTML code in the textarea and I need the html code to do something in the front end. However, the code gets displayed in text-format.
Example:
I have the following ul list
item 1
item 2
item 3
I need this to be displayed as an unordered list in the front-end. But what I get is the code displayed in text-format.
I know I have the option of changing the field type to ‘wysiwyg’, but I need to create several custom fields in “page” view and I would prefer if they were ‘textarea’ instead of ‘wysiwyg’.
Please let me know if this is possible and if yes, how would I go about doing it.
Thank you in advance.
Best,
Artin H.
Bill Erickson says
When rendering it on the frontend, you’ll have to use the html_entity_decode() function. Ex:
$embed = get_post_meta($post->ID, ’embed’, true);
echo html_entity_decode( $embed );
Artin H. says
Thank you Brill.
Everything looks great.
Artin H. says
Hey Bill,
I’ve been using your code and it’s working great. Thank you very much.
I do have one question. In one of my themes, I have added a ‘Radio button’ with two values: Yes/No
array(
‘name’ => ‘Radio inline’,
‘desc’ => ‘Please choose Yes or No’,
‘id’ => $prefix . ‘test_radio’,
‘type’ => ‘radio_inline’,
‘options’ => array(
array(‘name’ => ‘Yes’, ‘value’ => ‘Yes’),
array(‘name’ => No’, ‘value’ => ‘No’),
)
),
How would I check for the value?
Depending on the value, I’m going to either show something or not.
Here is how do the an if/else statement when it’s a regular field or a textarea:
if ( get_post_meta($post->ID, ‘cmb_xyz’, true) ) :
echo get_post_meta($post->ID, “cmb_xyz”, true);
endif;
But I’m not sure how to check for the value of a radio button.
If the value is “Yes” show this
else show nothing
Thank you in advance.
Best,
Artin H.
Bill Erickson says
To answer your specific question, you would do it like this:
$test_radio = get_post_meta($post->ID, 'test_radio', true);
if ($test_radio == 'yes')
echo 'Yes';
elseif ($test_radio == 'no')
echo 'No';
Instead of using a radio with Yes or No, I’d rather use a checkbox. Then you could just check to see if the custom field has a value.
Artin H. says
Great, working perfect.
Also, thanks for the “checkbox” suggestion, that makes more sense.
Charlie Chauvin says
Bill,
Just curious, I’ve been really enjoying the Custom Meta Boxes, but noticed that the ‘wysiwyg’ option is a little more limiting than the WordPress default where you can insert an image within the text and/or toggle the HTML view. I understand why you would do this, but is there a meta box option that could would include these like the wordpress?
Thanks.
Bill Erickson says
I don’t think it’s possible to duplicate the WordPress post editor with the media library. Even if you could add the media library to the alternate WYSIWYG, it would require some serious customizations to the media library to make the image not automatically inserted into the main post editor instead of your alternate one.
If you need the full WordPress editor I’d recommend finding a way to use just the post editor instead of an additional WYSIWYG. For instance, if you’re trying to do multiple content columns, use a content filter or shortcode.
trish says
thanks so much for this! I love how elegantly it works. I have a question though. I’ve created two custom fields, one for first name & one for last name. I’ve also hidden the post title from the editor. Now I would like to combine the first name & last name fields to give me the post title, such that it would replace WP’s post title field. (Right now, the way I have it set with the post title field hidden in the editor, I am getting ‘Auto Draft’ in the listings in the admin.) I think I need to set up another field that combines both the first name & last name but I’m unsure how that would be set up in the function with the arrays or how it would insert into the Post Title field. Does that make sense? Thanks again!
trish says
As a followup, it looks like if I can name one of the fields post_title, then combine first name & last name fields, that might do the trick: http://wordpress.stackexchange.com/questions/12056/automatically-set-post-title-to-same-value-as-a-meta-box
I’m still not sure how to implement it within the array.
Bill Erickson says
It’s beyond the scope of this post, but there should be a way to hook into the post title when the post is saved (hook = save_post) and use your post meta like this:
$title = $first .’ ‘. $last;
trish says
Thanks so much for pointing me to the right direction. For any of your readers who may be interested in the solution, this is what worked for me:
add_filter(‘title_save_pre’, ‘save_new_title’);
function save_new_title($people_title) {
if ($_POST[‘post_type’] == ‘people_profiles’) :
$fname = $_POST[‘_fname’];
$lname = $_POST[‘_lname’];
$fnamelname = $fname.’ ‘.$lname;
$people_title = $fnamelname;
endif;
return $people_title;
}
I really appreciate your help and the custom metaboxes!!
Steven Querry says
Hello Trish,
I would love to be able to accomplish this as well. However I’m new to all of this. I have 5 meta boxes I would like to put together to make the title. I think I’m following along till you get to the first $fname. Could you provide further? I tried this with no luck to start:
add_filter(‘title_save_pre’, ‘save_new_title’);
function save_new_title($vehicle_title) {
if ($_POST[‘post_type’] == ‘vehicle’) :
$vehicle_year = $_POST[‘vehicle_year’];
$vehicle_color = $_POST[‘vehicle_color’];
$vehicle_yearvehicle_color = $vehicle_year.’ ‘.$vehicle_color;
$vehicle_title = $vehicle_yearvehicle_color;
endif;
return $vehicle_title;
}
In my create metaboxes action I have a prefix of “sq_” – does that need to be included in the above script? Also where does this add_filter go? Does it need to be nested with my create_metaboxes or create_my_post_types?
Thank you so much for you help…
Bill Erickson says
I’m sorry, I don’t have any experience editing the title on save so I can’t help. But why edit it there? Allow the user to construct the title however they want and then on the frontend add the vehicle year and color before displaying the title.
trish says
Hi Bill,
I have another question regarding the image file upload field. I don’t know if I’m doing something wrong but it doesn’t seem to be working. I’ve got the other fields set up correctly and are being entered and saved properly. I didn’t do anything but copy and paste the code from example-functions file. The field itself is showing up in the editor; the issue is that when I click on ‘Upload File’ button, nothing happens. Am I missing something? I’m working off my localhost so I don’t know if that has something to do with it.
Thanks again!
Trish
Bill Erickson says
I’m not sure if the image upload one is working yet. I remember Jared Atchison looking into it. You can try contacting him: [email protected]
Vayu Robins says
Hi.
Trish did you mail Jared about the file upload function?
Bill, do you know if this function is working yet?
Vayu
Bill Erickson says
I’m out of the country so haven’t been keeping up with the CMB development, but my understanding is that it isn’t working at the moment. What I would use is the default media uploader, then once uploaded copy the image url and paste it in a text field.
Mike says
Well I think I found one senario:
If you name your theme like “My Theme”, it is not posible to access the jquery.cmbScripts.js file and you will get a 403 message.
Name your theme always without spaces, remove the space or use underscore: “MyTheme” or “My_Theme”, because it’s space sensitive!
Good Luck 😉
Bruce says
Trish,
Did you ever find a solution to the image file upload field?
I have the exact same issue. I too am running on localhost (XAMPP).
I have discovered in Firebug that there is a 403 access error on the file – jquery.cmbScripts.js?ver=3.1.3.
I have emailed Jared and am trading emails with him now on it
Thanks
Paul de Wouters says
Bill
what if I want one set of fields for a CPT, and another set of fields for pages?
Danny G Smith says
Anyway, I need to have multiple uploads, and it looks like the are being stored as a two dimensional array in the meta fields so it should be possible, but I am not sure how to get it to work in the front-end. is there something that is intuitive that I am missing? The way I am doing it is uploading the file first using the upload box, coping the url, pasting it in the box again, and then it is there below with links to remove, etc. If I try to duplicate the steps again, it appears to replace the first one with the current. By the way, I am going to post this on Bill’s blog as well, in case you see it there first.