Tutorial Details
- Technology: WordPress
- Difficulty: Moderate
The WordPress community is buzzing with excitement over the soon-to-be-released WordPress 3.0. Currently in Beta 2 now, WordPress 3.0 will have a lot of exciting new features , such as a new default theme and better menu management. Quite possibly the most exciting of these features is custom post types. In this tutorial, we’ll talk about creating and using custom post types to make a rock-solid theme.
What is a Custom Post Type?
Well, according to the WordPress Codex:
“Post type refers to the various structured data that is maintained in the WordPress posts table. Custom post types allow users to easily create and manage such things as portfolios, projects, video libraries, podcasts, quotes, chats, and whatever a user or developer can imagine.”

Essentially, it allows us developers to make new kinds of posts similar to the post and page types, which all appear in the main navigation in the WordPress admin. There are several advantages to this; most notably, we no longer need plugins to create special types, we can build a theme that relies less on custom fields (as we know them), and they make managing the site easier for clients and non-technical users. Instead of telling them to create a “post” and make sure to fill in all kinds of custom fields for say, music, we can simply tell them to click “Music” to add a new music post.
Let’s Get Started!
In this tutorial we will:
- Create a Custom Post Type for Products with our own inputs
- Create a custom “taxonomy” for the type.
- Create a theme template to go along with the new type.

Register the Custom Post Type
All of this will be done from within our theme’s functions.php file. I’m modifying the default 3.0 theme, TwentyTen.
The first thing we will do is tell WordPress that we want to register a new custom type. Here’s the code:
add_action('init', 'product_register');
function product_register() {
$args = array(
'label' => __('Products'),
'singular_label' => __('Product'),
'public' => true,
'show_ui' => true,
'capability_type' => 'post',
'hierarchical' => false,
'rewrite' => true,
'supports' => array('title', 'editor', 'thumbnail')
);
register_post_type( 'product' , $args );
}
The first line is a hook to tell WordPress that we want to call the function product_register() during initialization. It’s in that function that we register the new post type.
The function register_post_type() accepts two arguments: the name we want to give our post type, and a list of arguments used to create that post type, which we put in an array called $args. You can read exactly what all of the arguments are here, but I want to point out the important ones.
- label & singular_label: These are the labels as we want them to appear in the WordPress admin. ‘label’ will show up in the admin nav and anywhere that references multiple entries of that type (Edit Products, for example). ‘singular_label’ will show up when one of that type is referenced (Add Product, for example).
- capability_type: This tells WordPress which native type (post, page, attachment, revision, or nav-menu-item) the custom type will behave as. By making it a ‘post’ type, we can do things like add it to a category.
- rewrite: Tell WordPress if (or how) to apply permalinks formatting. You can send a boolean as we did, or any array of arguments to apply a custom permalink format to the type.
- supports: This is everything on the add/edit page that will show up. We want to have a title, editor(the content), and thumbnail images. Next, we will add our own custom inputs by cleverly masking custom fields as input fields for our custom type.
Adding Our Own Inputs

Let’s add our own custom inputs for our new type. Since we can now create new post types, we can make the custom fields more streamlined for users that might not be as familiar with WordPress as we are. It’s worth noting here that this functionality has been available since 2.5 and up until this point, has been used primarily by plugin developers. Here we are going to add a price field.
<?php
add_action("admin_init", "admin_init");
add_action('save_post', 'save_price');
function admin_init(){
add_meta_box("prodInfo-meta", "Product Options", "meta_options", "product", "side", "low");
}
function meta_options(){
global $post;
$custom = get_post_custom($post->ID);
$price = $custom["price"][0];
?>
<label>Price:</label><input name="price" value="<?php echo $price; ?>" />
<?php
}
function save_price(){
global $post;
update_post_meta($post->ID, "price", $_POST["price"]);
}
?>
Once again, the first couple of lines are hooks to tell WordPress when we want to use certain functions. The first line says that when the admin panel is initialized, call the function that we wrote, admin_init(). This function tells WordPress to add an area called “Product Options” to any posts of type ‘product’, and to use the function meta_options() to print the form fields. You can read more about add_meta_box here. meta_options() will then get any preexisting custom values and print the form field. The second action line states that when a post is saved, call our function save_price(), which uses update_post_meta() to add or update a custom field called ‘price’.
Custom Categories and Edit Columns

Our last step in creating a completely custom type is giving unique names to its category and edit column labels. First, the custom category name, or ‘taxonomy’.
register_taxonomy("catalog", array("product"), array("hierarchical" => true, "label" => "Catalogs", "singular_label" => "Catalog", "rewrite" => true));
The function we use is register_taxonomy(), which you can find in the codex here; it has been available since 2.8. It’s essentially saying that we want to create a new category type called ‘catalog’ which we will associate with the ‘product’ type. The last argument is an array of information similar to what we saw the register_post_type() function. When all is said and done, we will have the term ‘Catalog’ appear beneath our Products menu in the WordPress admin and it will behave like Post Categories do.

Next, we want to create a custom set of columns for our Product type.
add_filter("manage_edit-product_columns", "prod_edit_columns");
add_action("manage_posts_custom_column", "prod_custom_columns");
function prod_edit_columns($columns){
$columns = array(
"cb" => "<input type=\"checkbox\" />",
"title" => "Product Title",
"description" => "Description",
"price" => "Price",
"catalog" => "Catalog",
);
return $columns;
}
function prod_custom_columns($column){
global $post;
switch ($column)
{
case "description":
the_excerpt();
break;
case "price":
$custom = get_post_custom();
echo $custom["price"][0];
break;
case "catalog":
echo get_the_term_list($post->ID, 'catalog', '', ', ','');
break;
}
}
The first two lines are hooks to tell WordPress that we want custom columns for the ‘product’ type. The first line says that when printing columns for the product type, use the ones defined in the function prod_edit_columns().
In prod_edit_columns(), we have a key-value array where the keys are used to reference certain post information, which we define in the second function, prod_custom_columns(). The values in that array are the column headings. You might notice that prod_edit_columns() lists five columns, but we only describe display information for three in prod_custom_columns(). ‘cb’ and ‘title’ are part of a set of default keys that WordPress already has associations for. WordPress doesn’t know what the other three are, so it’s up to us to define them.
Making the Theme Template

Not too shabby, right? And now we are finally up to the fun part- the theme template. To make a theme template for a custom post type, we simply name the template single-<post-type-slug>.php and add it to our theme. In our case, this would be single-product.php. Here I will show you a snippet of that page that displays all of the information we’ve added to our custom post type:
<?php the_post(); ?> <?php $custom = get_post_custom($post->ID); $price = "$". $custom["price"][0]; ?> <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <h1 class="entry-title"><?php the_title(); ?> - <?=$price?></h1> <div class="entry-meta"> <div class="entry-content"> <?php the_post_thumbnail(); ?> <?php the_content(); ?> </div> </div>
I’ve added this code to the Twentyten theme in WordPress 3.0 Beta, as it’s the only theme that supports the new functionality- namely the menu system. I copied the single.php template, renamed it single-product.php and replaced everything in the ‘content’ div with the code above. To test my code, I went to Themes->Menus and added our new type to my site’s navigation. Then, I clicked through to our custom post type.
Wrapping Up
As I said previously, WordPress 3.0 is still in beta (you can get it here); so there are still some bugs to work out and some things may change in the final version. The best thing you can do is get in there and play with some of the new features to familiarize yourself with the updates/changes. From what I’ve seen so far, things are looking pretty good!



RoyalSlider – Touch-Enable ... only $12.00 
I can confirm that the data in the meta fields is being reset and it seems to happen after I’ve made some edits and re-uploaded functions.php. The scary thing is that ALL of these meta fields get flushed, not just for a particular post.
In other words, imagine that you’ve been relying on these things and made hundreds of posts. Then they all disappear at once. Yikes!
Anyone have any ideas? I definitely can’t rely on this tutorial at this point.
Guess by now you already know how to solve it.
I found the same problem. To solve it I just added a condition on the save function.
That is
if(isset($_POST["date"]))
update_post_meta($post->ID, “horario”, $_POST["date"]);
I think that for a reason this function is called when loading the page.
since $_POST["date"] does not exist in that call this is deleted on database.
Hope this work for you.
Regards
Thanks a lot! I have been struggling with this for a while and I just thought to look at the comments of this blog post. My problem has been fixed!
Could you post the full code please? I am not sure where to put the above line. And I don’t really know how to implement the solution by Mikoder either. Thanks!
@R0 – I actually followed a TUT from another site, and was having the same issue of the Meta Data not saving. Thank you so much for the fix!
According to a trace ticket related to this, you should use a hidden nonce field and check against that instead as it’d be set in the same areas as $_POST["date"] anyway and it’s more secure!
The way you write your posts is confusing… I’m always guessing whether you amending new lines of code or replacing previous ones… it would help if you just listed a completed version at the end.
So, the code only function WP 3.0? Because I’m using in the WP 2.9.2 but the code not function. Why?
Yup- this is 3.0 only. The new functionality is added there.
The custom post types are new in WP 3.0, so that is why it will not work with WP 2.9.x.
Great tutorial.
Is there a way to make the custom columns sortable or filterable?
If I create a custom post, how can I filter my query to a specific category?
Let’s say a create a custom post type called ‘Music’ and I create 3 categories : ‘jazz’, ‘classical’ and rock. How can I only display the ‘jazz’ and ‘rock’ entries??
HELP
This code is flawed perhaps amending the post would be beneficial to readers who may use this in a project and not realize their data will be gone without knowing why?
Nice post but i am having problem using it as i get an error in the code once i add the second code with the price label.
Would be nice to see how you implemented all the code in the function.php in the twentyten theme.
ditto
WordPress 3.0 is out, final, now.
Great tut btw! Very well written…!
After creating the taxonomy, I had to update the permalinks to work …
This only happened to me?
It would be good if there was some sort of UI for this rather than coding it yourself, similar to the form builder plugin perhaps. It’s still amazing functionality although i’m slightly put off that all the data can be flushed according to one of the comments here. That shouldn’t ever happen surely?
I don’t fully trust this, I can imagine maintaining the code could be a potential pitfall in the future too, sometimes you want users to be able to update drop downs themselves and i’m not sure this would be possible.
Hello, which part of the functions.php should i place the “Adding Our Own Inputs”‘s code?
Hi,
Similar to eliZZZa, I’m not seeing my custom taxonomies showing up on the Appearance->Menus page (/wp-admin/nav-menus.php) when filling out my nav.
I’m using the TwentyTen theme, and the only options I have for menus are Custom Links, Pages, and Categories, where the latter only shows those categories created under Posts->Categories.
I have defined two custom taxonomies, designated them as public (which according to the code sets the ‘show_in_nav_menus’ boolean to true in the register_taxonomies() function). These taxonomies show up in the Add New Post UI for my custom post type, so I know they are getting into the database.
However, I can’t seem to add my taxonomies as a Menu item.
Any thoughts?
Thanks!
Actually I figured it out. If anyone else has the issue, you have to open the ‘screen options’ tab at the top of the admin menu, and you can then select your new taxonomies show they show up on the menu screen.
@Joe, thanks for this post. I installed my 3.0 yesterday and despite one glitch on one of my blogs, things appear to be working fine on the others.
With regards to the custom post types, I originally thought that they would be built in to the user interface as some people have mentioned; coding it does seem a bit like Im working with Magento haha so its not as straight forward as Im use to with WordPress.
But, at least we’ve got the options now and there’s lots of tutorials and Im sure way more to come about working with the 3.0 API.
thanks Joe!
Hmm not sure if I am just thick… but when you call the custom inputs, where are you putting that code?
Great article! I’m trying to add an upload box similar to the price field, say for a product manual download. Is there an easy way to do that? I know there is a custom post type called attachment, but hardly any info is on it. Thanks!
I’m looking forward to sharing some cool stuff with you guys.
Thanks for building a great forum to share our tips.
Check out my site at http://fastvideoprofits.com
Amazing article. BTW, love your theme. Anyway. I followed this but couldn’t get the thumbnails to work. Any ideas. I did the whole thing three times. No thumbnails.
Same here. Have you figured out a solution yet?
I found the answer to this in the twenty ten theme. The thumbnails will work after the ‘after_theme_setup’ hook. Look through the functions.php file from the twenty ten theme for a more detailed explanation.
I used the following code for my own theme and it works fine. Just add it to your functions.php file:
add_action( ‘after_setup_theme’, ‘your_function_name’ );
if ( ! function_exists( ‘your_function_name’ ) ):
function your_function_name() {
// This theme uses post thumbnails
add_theme_support( ‘post-thumbnails’ );
}
endif;
I’m experiencing a minor bug:
After updating my post (a custom post type, with two custom meta fields), and making no more edits, then clicking away from the edit post screen, WordPress prompts me saying all un-saved edits will be lost. Confirm?
I click yes, and continue away from the edit post screen. No data is lost (since I already saved earlier).
I would like remove this unnecessary confirmation prompt.
Anyone else experience this?
Any ideas what might be causing this?
Nice Post Man, been really good not only learning about custom posts but modifying admin area and working with the db too.
One big problem though, as others have mentioned, the custom fields are being flushed by the AJAX autosave. I’ve checked the post data and my new values aren’t being sent, however the db fields are being updated – to nothing.
Could we get a heads up on what’s wrong here, kinda makes the whole thing unusable.
Anyways, nice post all the same, cheers for putting it together.
Can you enable the sticky feature somehow on custom post types like on the default posts?
This feature might be included in the next minor release.
See http://core.trac.wordpress.org/ticket/12702
I’m making a discography using this method and was wondering if there was a way I can add song titles to a custom field and have them displayed in a table that automatically adds rows depending on the number of titles somehow? Also, for some reason, in the following line, all of the sections come out fine but the thumbnail section is missing. Any ideas? Thanks for this post. It helped me tremendously.
‘supports’ => array(‘title’, ‘editor’, ‘thumbnail’)
ok, Howto unregister_post_type or remove_post_type??? :)
ty
!!! wordpress does not make queries to DB on register_post_type !!!
and for unregister post type simple not call register_post_type on init
additionally, we can use
function remove_post_type($post_type_name) {
global $wp_post_types;
unset($wp_post_types[$post_type_name]);
}
Looking for a free dictionary program for my computer…
Microsoft word is not working properly, is there any other free ones i can use?
What Do You Think Your Mom and Dad Have in Common?
“Both don’t want no more kids.” -Lori, age 8
Here begins affordable seo. Google introduced Google Sitemaps so that Web developers can publish lists of links from all of their sites. The basic idea is that some places a large number of dynamic pages which are available only by custom forms and user input. sitemap files can then be used to specify a web crawler how such pages can be found. Google, Yahoo, MSN and Ask now jointly support the Sitemaps protocol.
Our content management system(Joomla – The CMS we use) can help you get the URL with the right format. Check out the following link about SEO: http://pligg.net23.net/story.php?id=11335
For customers with an existing site, we provide a free SEO audit. The SEO Audit Committee provides valuable information on how your site ranks on major search engines and what changes are needed to improve your search engine ranking.
This really is a great tutorial. I have implemented it and it works like a charm – except for the fact that all my data in my custom fields is lost from time to time (due to Autosave or Quick Edit I am not sure). Could you post a correction to stop the flushing please?
I had the same problem that autosave (and quick edit) flushed the meta data. Some research on the issue revealed this: http://www.mikoder.com.au/2009/07/disappearing-custom-post-data/.
Hope that does it for you guys. Worked for me at least. :)
I just put in a WP trac ticket for this:
http://core.trac.wordpress.org/ticket/14192
Also on that ticket is a plugin to add a complete custom post type with custom meta boxes etc. so that’s a small freebie to anyone who wants to see how I do it.
IMO I think keeping custom post types to a plugin is a better idea than in a themes functions.php file!
If you are making something that could be used on any theme, then yes a plugin sounds like the right answer.
But, Im creating a child theme for Twenty Ten and needed a custom post type for the portfolio. The portfolio is part of the theme its self. So, the custom post type belongs in the functions.php. I also created page templates that support the new custom post type, as part of the child theme.
The idea behind this is when I release the theme, it will work right out of the box with out the need to download 3rd party plugins.
-James Tryon
Hi.
Has Anyone “How Did A 16 Year-Old-Girl Went From Dead-Broke Loser To Internet Millionaire Sensation?”
http://www.makecashguides.com/rich16/
Is it a scam or legitimate? Please leave your review:
hi to everyone,
I’m triyng to make some custom posts for my site and I have an annoyng issue.
when I register custom columns for visualization in backend I see a duplication of contents. That don’t happens when I register only one custom post.
the code for registering the “prodotto”columns is:
add_filter(“manage_edit-prodotto_columns”, “prod_edit_columns”);
add_action(“manage_posts_custom_column”, “prod_custom_columns”);
function prod_edit_columns($cols_prod){
$cols_prod = array(
“cb” => “”,
“title” => “Nome prodotto”,
“description” => “Descrizione”,
);
return $cols_prod;
}
function prod_custom_columns($col_prod){
global $post_prod;
switch ($col_prod)
{
case “description”:
the_content();
break;
}
}
and the one for registering the “news” columns is:
add_filter(“manage_edit-notizia_columns”, “notizia_edit_columns”);
add_action(“manage_posts_custom_column”, “notizia_custom_columns”);
function notizia_edit_columns($col_news){
$col_news = array(
“cb” => “”,
“title” => “Nome notizia”,
“description” => “Descrizione”,
);
return $col_news;
}
function notizia_custom_columns($cols_news){
global $post_news;
switch ($cols_news)
{
case “description”:
the_excerpt();
break;
}
}
as you can see the posts are basically the same, it’s only a matter of usability.
thanks for support.
and cb have the same code as the tutorial (but here isn’t show)…
gotcha! i fond the solution!
i just added an if statement before the “case” cycle to determine what kind of post i’m showing. so the code became:
function notizia_custom_columns($cols_news){
global $post_news;
if( get_post_type() == ‘notizia’)://here goes the name of custom post
switch ($cols_news)
{
case “description”:
the_excerpt();
break;
}
endif;
}
and that prevents the duplication of contents under the columns.
I am using Custom Post Types for product display. My products have sub categories like Main Category -> Sub Category. Do I need to configure custom post type for every sub category?
I tested it. I think all catalog items would use same page template? In Themes->Menus, will it automatically show me all categories which are in catalog or do I need to compose menu using custom link?
How can I show further options of post? like Discussions, Send Trackbacks and other options which some plugins add like All in one SEO?
Hi!
I’ve create great car catalog with this tutorial, but I’ve got a problem.
I want to create not a input with text (like price in this tutorial) but select with few option.
I want create dropdown list with car body like sedan, hatchback, coupe :)
Now, I’ve got this but it’s doesn’t work:
<select name="car_body" value="” >
<option value="” selected=”">sedan
<option value="” selected=”">hatchback
I’ve try so many times, I have no idea what to do :/
Anyone can help me?
Greetings for great article!
If these are your exact lines of code for your select it does not work because you don’t have any VALUES.
Read this : http://www.w3schools.com/TAGS/att_option_value.asp
Hello,
looking to buy spyeye for a cheap price? including lifetime updates contact me on ICQ# 614191772
1. Run he keygen.exe 2. Generate keys and add it your Kaspersky Product. 3. Restart Computer 4. It’s Done…..Enjoy the Lifetime license without getting Blacklisted. Plz leave a comment of thanks if it has helped you. Download Link here:
Hi,
So I know that Google has a discussion search feature, I am also familiar with Boardreader & BoardTracker – both are fine, but I am looking for a search engine for forums that I can:
1. Filter results by time frame
2. Filter results by number of posts & users
3. Search only in specific forums (like on fashion related forums)
Does anybody know if such a service exists?
Thanks,
Adam
How can i make have a page that shows ALL the posts of the “product” type ?
Thanks for the good tut
Instead of going though ALL of this code, most of which is unstable at the moment, its best just to go with:
For Custom posts
http://webdevstudios.com/support/wordpress-plugins/
For custom fields
http://eskapism.se/code-playground/simple-fields/
Saves a lot of time and effort.
This is greate post!!!
Can u provide full explanation on where to put each code!!!
so that beginners like me can understand!!!!
thank you so much….
Goddamn Matt, you’re rude. If you have a problem with the code, contribute to fixing it instead of trying to cause an argument with the author.
Nice tutorial. I have no doubt I will make some changes to the code to get it to work in the best way possible, but this is a nice start.
Kris
Hack again?!
can u give the code as dowmloaded for us to see as working .
I really love that feature of WordPress 3.0 it make it much more powerful!
Great article!
One question: How would you create a page that lists all your “products” but not “posts” “pages” or “foos”?
This is actually working quite well for me. The only issues I am running into are with the custom columns and for some reason I cannot figure out how to query my custom post type to display say 5 or 15 or all of the posts from this type.
My issue with the custom columns is that I cannot for the life of me get the custom information from my custom post into the custom columns
Any ideas on these? (the query problem being most important)
Very good tutorial. Thank’s Jo.
LOOKING FOR “PRICE” INPUT FIELD ?
If like me you are looking for the “added price input” search no more… It took me a wile to figure out that the custom fields are now (by default) shown in the right collumn of the EDIT page.
–
ADDING A CUSTOM ICON TO YOUR NEW CUSTOM POST TYPE
A created a (100% legal free) icon for this “product” post-type. You can get it here : http://twitpic.com/2bbana
And here is the code to paste in your theme’s functions.php (after the code from this tutorial) :
/* Customize WordPress Dashboard Icons for custom post types */
function wp3_custom_post_icons() {
echo ‘#adminmenu #menu-posts-product div.wp-menu-image { background-image: url(‘.get_bloginfo(‘template_directory’).’/images/adminmenu-posts-product.png); background-position: 5px -33px; } #adminmenu #menu-posts-product:hover div.wp-menu-image, #adminmenu #menu-posts-product.wp-has-current-submenu div.wp-menu-image { background-position: 5px -1px; }’;
}
add_action(‘admin_head’, ‘wp3_custom_post_icons’);
yes, the pricing / custom field is in the right column, how to move it under description, any suggestion
Thanks
well!
Thanks a lot for writing it so clearly, My theme (SWIFT) will benefit a lot from the points you covered here.
http://www.choicemeds.info
WordPress kills me. I’m programming PHP for at least 10 years now, but needed half a day to unterstand a short tutorial. Damn!