
Learning how to create custom WordPress meta boxes allow you to make professional UI elements for yourself and your clients. This WordPress meta box tutorial will show you how to add admin UI elements to the edit post/page screens.
Important Before you begin reading, you might want to read Take Your WordPress Meta Box to the Next Level. This PHP class will let you create meta boxes fast with the flexibility you need as a developer. Full documentation walks you step-by-step. Create custom WordPress Meta Box UI elements for your projects with ease.
Creating a custom WordPress meta box lets you make clean UI elements for you and your clients. The default WordPress custom fields are functional, but IMHO, and if you can pull it off, using a WordPress meta box is the way to go. It will give your project that professional touch.
This WordPress meta box tutorial will show you how to quickly add a clean UI for some custom fields. You will even learn how you can hide these fields and prevent them from appearing in the custom fields area.
Getting Started with WordPress Meta Boxes
The code for your meta box will pretty much go in one of two places: in a plugin file or in your functions.php file. This tutorial will cover the latter (for those of you creating plugins, you will be able to adapt the code for your use as well). I like tutorials with code examples and lots of comments, with that said, lets dive right in:
I recommend that you download the sample files or cut + paste the code and markup below in the appropriate places as indicated. There are three sample files (you will also need to create a folder and name it custom):
/current_theme_folder/functions.php /current_theme_folder/custom/meta.php /current_theme_folder/custom/meta.css
/current_theme_folder/functions.php file:
<?php
define('MY_WORDPRESS_FOLDER',$_SERVER['DOCUMENT_ROOT']);
define('MY_THEME_FOLDER',str_replace("\\",'/',dirname(__FILE__)));
define('MY_THEME_PATH','/' . substr(MY_THEME_FOLDER,stripos(MY_THEME_FOLDER,'wp-content')));
add_action('admin_init','my_meta_init');
function my_meta_init()
{
// review the function reference for parameter details
// http://codex.wordpress.org/Function_Reference/wp_enqueue_script
// http://codex.wordpress.org/Function_Reference/wp_enqueue_style
//wp_enqueue_script('my_meta_js', MY_THEME_PATH . '/custom/meta.js', array('jquery'));
wp_enqueue_style('my_meta_css', MY_THEME_PATH . '/custom/meta.css');
// review the function reference for parameter details
// http://codex.wordpress.org/Function_Reference/add_meta_box
// add a meta box for each of the wordpress page types: posts and pages
foreach (array('post','page') as $type)
{
add_meta_box('my_all_meta', 'My Custom Meta Box', 'my_meta_setup', $type, 'normal', 'high');
}
// add a callback function to save any data a user enters in
add_action('save_post','my_meta_save');
}
function my_meta_setup()
{
global $post;
// using an underscore, prevents the meta variable
// from showing up in the custom fields section
$meta = get_post_meta($post->ID,'_my_meta',TRUE);
// instead of writing HTML here, lets do an include
include(MY_THEME_FOLDER . '/custom/meta.php');
// create a custom nonce for submit verification later
echo '<input type="hidden" name="my_meta_noncename" value="' . wp_create_nonce(__FILE__) . '" />';
}
function my_meta_save($post_id)
{
// authentication checks
// make sure data came from our meta box
if (!wp_verify_nonce($_POST['my_meta_noncename'],__FILE__)) return $post_id;
// check user permissions
if ($_POST['post_type'] == 'page')
{
if (!current_user_can('edit_page', $post_id)) return $post_id;
}
else
{
if (!current_user_can('edit_post', $post_id)) return $post_id;
}
// authentication passed, save data
// var types
// single: _my_meta[var]
// array: _my_meta[var][]
// grouped array: _my_meta[var_group][0][var_1], _my_meta[var_group][0][var_2]
$current_data = get_post_meta($post_id, '_my_meta', TRUE);
$new_data = $_POST['_my_meta'];
my_meta_clean($new_data);
if ($current_data)
{
if (is_null($new_data)) delete_post_meta($post_id,'_my_meta');
else update_post_meta($post_id,'_my_meta',$new_data);
}
elseif (!is_null($new_data))
{
add_post_meta($post_id,'_my_meta',$new_data,TRUE);
}
return $post_id;
}
function my_meta_clean(&$arr)
{
if (is_array($arr))
{
foreach ($arr as $i => $v)
{
if (is_array($arr[$i]))
{
my_meta_clean($arr[$i]);
if (!count($arr[$i]))
{
unset($arr[$i]);
}
}
else
{
if (trim($arr[$i]) == '')
{
unset($arr[$i]);
}
}
}
if (!count($arr))
{
$arr = NULL;
}
}
}
?>
/current_theme_folder/custom/meta.php file:
<div class="my_meta_control"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras orci lorem, bibendum in pharetra ac, luctus ut mauris. Phasellus dapibus elit et justo malesuada eget <code>functions.php</code>.</p> <label>Name</label> <p> <input type="text" name="_my_meta[name]" value="<?php if(!empty($meta['name'])) echo $meta['name']; ?>"/> <span>Enter in a name</span> </p> <label>Description <span>(optional)</span></label> <p> <textarea name="_my_meta[description]" rows="3"><?php if(!empty($meta['description'])) echo $meta['description']; ?></textarea> <span>Enter in a description</span> </p> </div>
/current_theme_folder/custom/meta.css file:
.my_meta_control .description
{ display:none; }
.my_meta_control label
{ display:block; font-weight:bold; margin:6px; margin-bottom:0; margin-top:12px; }
.my_meta_control label span
{ display:inline; font-weight:normal; }
.my_meta_control span
{ color:#999; display:block; }
.my_meta_control textarea, .my_meta_control input[type='text']
{ margin-bottom:3px; width:99%; }
.my_meta_control h4
{ color:#999; font-size:1em; margin:15px 6px; text-transform:uppercase; }
If you’ve set everything up correctly you should see the following meta box in a edit post or edit page screen:

Using The Meta Box Values In Your Template
Being able to create the values is just the first part, now you need to do something with those values. Most likely you will be displaying the values in your post or page templates.
Remember the example above uses a single variable (_my_meta) to store values as an array, so when you get the meta value back from WordPress it will be an array and you will have to access the values using array syntax. Doing this is pretty straight forward:
$my_meta = get_post_meta($post->ID,'_my_meta',TRUE); echo $my_meta['name']; echo $my_meta['description'];
The global $post variable should always be available, but here are some other methods you can use to get the current post or page ID which you will need to use with get_post_meta():
// using $post global variable
echo $post->ID;
// get a page by its path (recommended over using a page ID)
$page = get_page_by_path('company/contact-us');
echo $page->ID;
// if you are working in the loop
echo get_the_ID();
The above code and markup will help you produce clean UI elements for your next WordPress project. Like any tutorial, consider this a starting point for you to do great things. I hope you’ve enjoyed!

Nice tutorial, it’s working great! Thanks!
But I have a question for you. How can I use the WP_Query from here: http://codex.wordpress.org/Function_Reference/WP_Query#Custom_Field_Parameters
I have 2 checkboxes in my template and I want to display the loop if a checkbox is checked.. but I don’t know how to use that in meta_query
Can you help?
Thank you Dimas
@Cippo, hopefully you’ve read the messages up top and some of the comments which point others to wpalchemy … have a look at it for creating super easy meta boxes and also look at this for help with WP_Query.
In my rush, I didn’t see how simple it was … d’oh.
The query part for meta looks like this:
'meta_query' => array( array( 'key' => '_my_meta', 'value' => 'addtosidebar', 'compare' => 'LIKE' ) )and I was trying to make it ‘key’ => ‘_my_meta[addtosidebar’ and it didn’t want to work. Yes, I admin that I’m not a PRO at PHP
.. yet.
But THANKS for sharing the code, very helpful!
I did not understand how to use the file type in the class, so i continued using your first article.
i put this to test and does not return to me a value.
get:
return:
$new_file_data = $_FILE['_my_meta'];
print_r($new_file_data);
forget, i’m a stupid
i forgot the S on $_FILES
Hi,
Really flexible custom meta box solution, thankyou
I’m trying to create a select box, any ideas how to incorporate this with your script?
Matt
@Matt, I hope you’ve gotten a chance to take a look at wpalchemy, it’s the next incarnation of the above.
As a developer I like flexibility, to me flexible means a good mix between configuration and convention. I’ve tried to keep the form setup separate from the functionality, so just liek you would define input fields, you can do the same for select boxes, check boxes, raido buttons, etc.
Hi,
when add checkbox or radio button for example
with
jacket
t-shirt
update post and add in single.php
it’s ok but i=when return into admin area and try to change some value all field was reset.
Why my choice in admin panel is not save?
Sorry for my English
@Dimas
Thanks for the wpalchemy link, just playing around with it now, must say i’m highly impressed.
Cheers
I just put this code after my thesis functions on the functions.php and all my thesis meta boxes disappeared and it just added this meta box… =/
Hi Dimas
Would u be so kind to explain how the my_meta_clean() work ?
It calls itself inside the loop ?
i just want to understand how it works, rather than copy and use it.
any level of explanation is much appreciated.
@Paul, it basically loops a single or multi-dimensional array and cleans it up, looking for empty values. The function uses itself because it recursively checks any nested array values.
A user (Franz) of my wpalchemy class suggested the following tip: using
array_filter()(without the callback) also does a similar filtering … I am not sure if it will properly filter a multi-dimensional array but it appears to do a good job filtering.Also the function has changed a bit since my early versions (from above), the latest function looks like this (this is what wpalchemy currently uses):
https://gist.github.com/1068596
Hi Dimas
Thanks a whole lot for the explanation and the link to revised code.
( really appreciate your help and generosity )
hi i am creating a wordpress theme can you please help ?
the code is not working on the page of wordpress ,it work on the post ,
the box are show on every post and pages but the values not show in my template ,(yes i use the above code in my template )i check the html they retun nothing but in the post they working well
@fahim, I would really recommend that you use wpalchemy else try using http://gist.github.com to post your code so myself and other can review and help
there is nothing a new which i add in your code, i just download the code and put them according to instruction
i grab the values on the both template page
a blog template page (index.php) and the single.php a (a single blog post page),i set the index as a home page
the problem is that when i put the values on the blog page ( a wordpress page not a post ) page not show them
but when i put the values in the single post then both home page (index.php) and single post page(single.php) show them (both page and the post show the values)
i dont want to use meta box in post , i want to use them in the pages
for both page and post the $post->ID Work ?
i dont know anything about them , In the wordpress both page and post meta data access by get_post_meta function, there is no difference ?
Please Reply as soon as possible
how to disable drag & drop feature for metabox?
Thanks for this, it’s fantastic and very easy to setup.
I have one problem though, the meta data writes to the database okay, and displays on the site correctly, but when I go back into the Edit Page screen, the custom boxes are blank again.
Any idea why that would be?
Hi, Scrap that!!!
Just noticed I needed to change the meta names in the meta.php input boxes.
All working now, fantastic article. Thank you!
@Craig, I hope you’ve gotten a chance to take a look at wpalchemy, it’s the next incarnation of the above.
Hi, I hope Dimas or someone can assist with a problem I’m having. First let me describe what I’m up to: I am creating a custom post type for ‘portfolio’. Because the small thumbnails in the proposed portfolio galleries are not the same image as the one they will link to. I decided to use wpalchemy to create the custom meta boxes that would allow me to add two images at a time to a ‘portfolio’ post for the purpose of matching up the thumb with the larger image and then being able to drag and drop to reorder them.
I have also employed scripts to be able to access the wp media loader for each of these custom fields, upload an image and put it’s URL into the custom field.
Everything works, but one frustrating problem. Upon saving the post, the field sets duplicate so there are two of any recent additions when the edit page reloads… it is creating extra entries for a reason I can’t seem to track down…
A symptom that may point to part of the problem. When you go to create a new ‘portfolio’ the regular media upload button shows that there are no attachments to the post. However, when the media uploader pops up in relation to filling in the custom fields, it remembers every image that has been uploaded to this custom post type, as if there were attached to the new post… not sure if this is related.
In any case, there are three documents worth looking at (I posted on GIT hub: https://github.com/seahoss/shareCode), the three documents which contribute are: ‘functions.php’, ‘file-upload.js’, and ‘meta_portfolio_images.php’
Any help you can provide is greatly appreciated.
Thanks its a fantastic help
Hello! Any example for showing a list posts from a custom post type with custom meta boxes?
Thanks so much for this tutorial!