Create a WordPress custom post type
- Knowledge needed: PHP, experience with WordPress
- Requires: WordPress v3.1+
- Project time: 20 minutes
- Download source files
Web developer, software engineer and part-time entrepreneur Gilbert Pellegrom explains how to use custom post types in WordPress. In his example Gilbert builds a book database, which you can control via your WordPress admin area and which displays books on your site
Custom post types were introduced in WordPress v3.0 to allow developers to easily add different types of content to their plug-ins and themes, allowing you to easily extend WordPress and make it more like a CMS.
In this tutorial we will see exactly what custom post types are, and how to use them in your plug-ins and themes straight away.
Advertisement
What are custom post types?
In its simplest form a custom post type is a representation of a type of content. Don’t be confused by the fact that it has “post” in its title. It can literally represent anything you want. So for example, WordPress has several default post types built in:
- Posts
- Pages
- Attachments
- Revisions
- Nav Menus
Try to think of custom post types as a way to manipulate content in the same way that you would a blog post, but with far more control as to how it's displayed. As a word of warning though, if you’re simply looking to categorise your content in different ways, consider using categories, tags or custom taxonomies before jumping into creating custom post types.
How do I create a custom post type?
Creating a custom post type is actually incredibly simple, and when you see how much work WordPress does for you, you’ll begin to understand how powerful they can be. In your theme’s functions.php file, insert the following code:
That is literally all you need to create a custom post type. In the example above we are creating a custom post type called “book”, which we will use to create a book database. We give it some labels, set a few options, and we're good to go.
The example above is extremely simple but demonstrates the simplicity of creating custom post types. Now if you go to your WordPress admin panel, you should see a new menu item called “Books”. Go ahead and play around with the new “Books” section. Because we set “public => true”, all of the admin interface is generated for us by WordPress, making our lives much simpler.

Creating a book database
In our last example we created a very basic custom post type, but we were only scraping the surface of what's possible with custom post types. Let’s get our hands dirty and create a full-blown custom post type.
- // Create out post type
- add_action( 'init', 'create_post_type' );
- function create_post_type() {
- 'labels' => post_type_labels( 'Book' ),
- 'public' => true,
- 'publicly_queryable' => true,
- 'show_ui' => true,
- 'show_in_menu' => true,
- 'query_var' => true,
- 'rewrite' => true,
- 'capability_type' => 'post',
- 'has_archive' => true,
- 'hierarchical' => false,
- 'menu_position' => null,
- 'editor',
- 'author',
- 'thumbnail',
- 'excerpt',
- 'comments'
- )
- );
- register_post_type( 'book', $args );
- }
- // A helper function for generating the labels
- function post_type_labels( $singular, $plural = '' )
- {
- if( $plural == '') $plural = $singular .'s';
- 'name' => _x( $plural, 'post type general name' ),
- 'singular_name' => _x( $singular, 'post type singular name' ),
- 'add_new' => __( 'Add New' ),
- 'add_new_item' => __( 'Add New '. $singular ),
- 'edit_item' => __( 'Edit '. $singular ),
- 'new_item' => __( 'New '. $singular ),
- 'view_item' => __( 'View '. $singular ),
- 'search_items' => __( 'Search '. $plural ),
- 'not_found' => __( 'No '. $plural .' found' ),
- 'not_found_in_trash' => __( 'No '. $plural .' found in Trash' ),
- 'parent_item_colon' => ''
- );
- }
- //add filter to ensure the text Book, or book, is displayed when user updates a book
- add_filter('post_updated_messages', 'post_type_updated_messages');
- function post_type_updated_messages( $messages ) {
- global $post, $post_ID;
- 0 => '', // Unused. Messages start at index 1.
- 2 => __('Custom field updated.'),
- 3 => __('Custom field deleted.'),
- 4 => __('Book updated.'),
- /* translators: %s: date and time of the revision */
- 6 => sprintf( __('Book published. <a href="%s">View book</a>'), esc_url( get_permalink($post_ID) ) ),
- 7 => __('Book saved.'),
- 8 => sprintf( __('Book submitted. <a target="_blank" href="%s">Preview book</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
- 9 => sprintf( __('Book scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview book</a>'),
- // translators: Publish box date format, see php.net/date
- date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
- 10 => sprintf( __('Book draft updated. <a target="_blank" href="%s">Preview book</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
- );
- return $messages;
- }
So what's going on here? Well, first off we're creating our custom post type and setting most of the available options with it. I’m not going to explain what all these options do here because the WordPress Codex explains them very well.
The second function you see is simply a helper function that makes it easy to generate the labels that will be used in the WordPress admin for the custom post type. You can manually set the $labels array if you wish, but I find this cleaner and easier.
Finally we're adding a filter to change the updated messages. So now whenever WordPress shows an update message for this custom post type, it will show right labels (rather than just using “post” all the time).

Viewing our books
So now we have this awesome custom post type that we can manage in the WordPress admin. But what about actually displaying books on your site? Well, WordPress has you covered.
There are several templates that can be used in your themes to automatically display our custom post type content:
- single-{posttype}.php - In the same way that posts are shown on their own page with single.php, custom post types will use single-{posttype}.php if it's available. So in our example above we could create a single-book.php file and the product posts would be shown using that template.
- archive-{posttype}.php - As of WordPress v3.1 you can also use an archive-type-template. In the same way that posts are shown on their own archive with archive.php, custom post types will use archive-{posttype}.php if it's available.
Now, having these template files at our disposal is great, but sometimes you need to get a bit more complicated and use a custom query for some fine-grained control. Thankfully, WP_Query has us covered by allowing us to specify a “post_type” parameter.
- $loop = new WP_Query( $args );
- while ( $loop->have_posts() ) : $loop->the_post();
- the_title();
- echo '<div class="entry-content">';
- the_content();
- echo '</div>';
- endwhile;

Conclusion
You now have a fully functioning book database, which you can control via your WordPress admin area, enabling you display books on your WordPress site. This tutorial only serves as an introduction to what can be achieved with custom post types. They are an incredibly powerful feature of WordPress, so use them carefully, but don’t be afraid to stretch your imagination: there are so many things that you can easily achieve using custom post types.
Further notes
Before we finish there are a few extra points that are worth mentioning:
- When creating custom post types it is considered best practice to prefix the post type with a unique “namespace” to avoid conflicts with other themes and plug-ins. Although be careful that the total length of your custom post type name isn’t greater than 20 characters, otherwise strange things will happen.
- Don’t use “wp_” as your namespace prefix as it is reserved for internal WordPress names.
- If you get 404 pages when you first try to see your custom post types, don’t panic. Simply visit the Settings > Permalinks page and this will flush your rewrite rules. If you're a plug-in author, make sure your flush the rewrite rules on your plug-in activation.




9 comments
Comment: 1
Wordpress really opened up for me once they added this functionality, I quickly found that clients greatly appreciate Custom Post Types instead of any complicated methods of using categories to "hack" in features they wanted. Besides the mild hassle setting them up as you want them, everything is made so much easier for everyone.
For post types which also have meta data which can also be fiddly for clients to add in it's native form, I tend to use Custom Field Template plugin to create forms within the edit page that updates meta concerning that post type, with the ability to add instructions and present options in appropriate contexts (such as limiting them to drop downs and radio buttons):
http://wordpress.org/extend/plugins/custom-field-template
While using a plugin for meta entry isn't as fool-proof as having a hard-coded custom meta box, the meta is at least still accessible through the native meta box if the plugin is removed for some reason.
Comment: 2
Comment: 3
Very good tutorial even though the difference between this method and the custom post type plugin is not explained.
Cheers!
Comment: 4
Comment: 5
Parse error: syntax error, unexpected T_STRING in /home/ ... REMOVED ;) ... /functions.php on line 5
Any idea why?
WP 3.2
Fresh install
WP toolbox theme
Thanks
Comment: 6
I followed your tutorial and am able to view my book example. However, the other variables, such as when the book was published and other meta stuff, are not made available within the Book write page. Thus, the only thing that views is the default stuff from my (copied from single.php file) single-book.php. (Yep, I'm sort of new to WP.) That said, what else is needed?
Thanks!
Comment: 7
Comment: 8
Good work! Your post is an excellent example of why I keep coming back to read your excellent quality content
write my research paper online
Comment: 9