A WordPress website can contain multiple content types, known as “post types” in the WordPress world. The default post types are “Post” and “Page”, but you can create your own custom post types.
On my website I’ve created a Code Snippets post type and a Projects post type. This simplifies the content creation process by grouping different types of content in the backend, and lets you have dynamic archives. I can customize the edit screen with Custom Metaboxes and Taxonomies.
Registering a post type
In your core functionality plugin, use the register_post_type()
function to create a post type.
<?php | |
/** | |
* Register the Testimonial post type | |
* | |
*/ | |
function be_register_testimonial_post_type() { | |
$labels = array( | |
'name' => 'Testimonials', | |
'singular_name' => 'Testimonial', | |
'add_new' => 'Add New', | |
'add_new_item' => 'Add New Testimonial', | |
'edit_item' => 'Edit Testimonial', | |
'new_item' => 'New Testimonial', | |
'view_item' => 'View Testimonial', | |
'search_items' => 'Search Testimonials', | |
'not_found' => 'No Testimonials found', | |
'not_found_in_trash' => 'No Testimonials found in Trash', | |
'parent_item_colon' => 'Parent Testimonial:', | |
'menu_name' => 'Testimonials', | |
); | |
$args = array( | |
'labels' => $labels, | |
'hierarchical' => false, | |
'supports' => array( 'title', 'editor', 'thumbnail' ), | |
'public' => true, | |
'show_ui' => true, | |
'show_in_menu' => true, | |
'show_in_nav_menus' => true, | |
'show_in_rest' => true, | |
'publicly_queryable' => true, | |
'exclude_from_search' => false, | |
'has_archive' => true, | |
'query_var' => true, | |
'can_export' => true, | |
'rewrite' => array( 'slug' => 'testimonials', 'with_front' => false ), | |
'menu_icon' => 'dashicons-groups', // https://developer.wordpress.org/resource/dashicons/ | |
); | |
register_post_type( 'testimonial', $args ); | |
} | |
add_action( 'init', 'be_register_testimonial_post_type' ); |
Labels
The labels
parameter lets you customize all the labels used throughout the WordPress backend. It’s usually as simple as find/replacing the plural and singular forms of the post type, but you may want to tweak some of the other labels more extensively.
Supports
The supports
parameter lets you specify which WordPress features should be enabled for this post type. Options:
- ‘title’
- ‘editor’ (content)
- ‘author’
- ‘thumbnail’ (featured image, current theme must also support post-thumbnails)
- ‘excerpt’
- ‘trackbacks’
- ‘custom-fields’
- ‘comments’ (also will see comment count balloon on edit screen)
- ‘revisions’ (will store revisions)
- ‘page-attributes’ (menu order, hierarchical must be true to show Parent option)
- ‘post-formats’ add post formats, see Post Formats
Show in REST
The show_in_rest
parameter lets you decide if this post type should be visible in the REST API. This must be set to true
if you’d like to use the new Gutenberg block editor
Has Archive
The has_archive
parameter enables the post type archive, a dynamic archive listing recent content in this post type.
Rewrite
The rewrite
parameter lets you enable and customize the URL rewriting for this content type.
Menu Icon
The menu_icon
parameter lets you specify a Dashicon to use as the icon in the admin area.
Using a plugin
Rather than coding it yourself, you can use Custom Post Type UI to create and manage your custom post types. The plugin lets you specify the post type slug, labels, and settings shown above.
Neil says
Great post. Bill, do you think there is a speed-to-load advantage in adding your own code in custom functions versus these custom post plugins? Or is any advantage out-weighed by their ease of use?
thanks
Bill Erickson says
That’s a good question, and I’ve never tested both options from a performance standpoint. I do know that once you use the Custom Post Type UI plugin to create the post type, you can remove the plugin and everything still works (it’s only used once to create it). I don’t think there’s a performance hit for using either plugin over hand-coding, but I can’t be sure.
Katarsis says
Hi, i ´am starting my own bussiness in develope wordpress site (i love this) and i have a question
do you update plugins in the customers sites? or disable the upg´s annoucement and do not upg never? or only when strongly necessary?
tks but i don´t know how to treat this topic in the future?
tks again and sorry my language (i am from uruguay)
Bill Erickson says
I try and minimize the use of plugins, but there are a few that I use often. Examples would be All in One SEO (for sites not built with Thesis or Genesis), Contact Form 7, and Akismet. Since I try to use well-established plugins, I don’t often update them because the updates aren’t (usually) security-based, they only provide new features. If the old plugin is working, no reason to upgrade and take a chance of it breaking.
Nanette says
Do you use both of these plugins you referenced with Thesis?
Bill Erickson says
Yes I do.
Nanette says
I tried followed your tutorial, thanks… but I am having a problem. When I pasted this code into my custom_page template I get errors. And I dont see any of the custom post.
Bill Erickson says
What type of error did you get?
I didn’t provide the complete code for displaying them on the page, so I’m not sure what code you copied.
Nanette says
I guess I need the full code that I would put int customfunctions.php (in Thesis.) Can you help me out?
I already have custom template that replaces my thesis_custom_template that I use for my home page. Is it possible to use a conditional tag that says if front_page use template “A” and then if product_page (which is my custom post type use template “B”?
Kyle Jones says
Hey Bill-
Great post! Exactly what I needed to get over the hump for listing all members of a staff directory (see this post).
My issue now is with sorting the member listing alphabetically by last name. Currently, it does it by latest post ID. Here’s a screenshot and this is the template file.
If you have any ideas I’d love to hear them.
Thanks!
~Kyle~
Bill Erickson says
Change query_posts to this: query_posts(‘post_type=staff-directory&showposts=-1&orderby=title&order=ASC’);
That will sort them by the title, from A-Z.
Kyle Jones says
Bill-
I ran across this a bit after posting, but my situation is that I don’t use the default title, I use meta fields name_last_name and name_first_name. So, I need to be able to sort by name_last_name.
Thanks!
~Kyle~
Bill Erickson says
Ah, then you should change query_posts to this:
query_posts(‘post_type=staff-directory&showposts=-1&meta_key=name_last_name&orderby=meta_value&order=ASC’);
ptim says
Hi Bill, thanks so much for your great resources (love core-functionality, in particular).
Just wanted to clarify – you’ve written: “You can customize this using the archive-ebook.php template file. For single posts, use archive-ebook.php.” Should that last line read single-ebook.php?
I’m was having trouble with the has_archives property – eventually found that I needed to flush the rewrite rules by hitting Save on the Permalink settings. Had me scratching my head – hope it helps someone 🙂
Bill Erickson says
Good catch! That was a typo
Jamie says
Dear Bill
i’m using a custom post type of ‘portfolio’ and the genesis framework.
the portfolio will be the home page (for a designers site so the latest work is up front)
i created a function in the home.php to show my ‘portfolio’ custom post type, BUT, other regular posts show too?
how can i only show the custom post types? or is this not possible with the home.php in genesis, might need to just do a page template, and set that as the home page.
thanks in advance
Bill Erickson says
home.php should really only be used for listing your blog posts. If you want portfolio items on your homepage, I recommend:
1. Creating a page called “Home”, then go to Settings > Reading and set it as the front page.
2. If you still want a blog, create a page called “Blog” and in Settings > Reading set it as the posts page (home.php will be used for this page)
3. You could either create a front-page.php template file for the front page, or a page template and then specify that the homepage is using that template. In this file, build your custom query.
Jamie says
Thanks for clearing that up Bill, you just saved me a few hours of my time, as i was completely on the wrong track. I get it all now.
Really appreciate your support 🙂
divya says
Hi Bill,
i want to make a recipe index with image and its title only.like this http://pinchofyum.com/category/recipes/dinner/quick-and-easy/page/2
how can i do this?
Jamie Mitchell says
Hi Divya
Are you using the genesis framework?
Chris says
Hey Bill, thanks for this great plugin!
Currently I have a banner image (featured image) after the Genesis header that it is pulling from for the posts in the grid. Is there a way to set a different image for the archive page teasers? I thought possibly adding max-height to 100% in CSS might do it, but something tells me I may need to set up something to crop it on upload…it’s all a bit new to me how to do this. Would appreciate any thoughts or suggestions on how to achieve this, thanks!
Chris says
Apologies Bill, I posted this query regarding your Grid Posts Plugin: https://www.billerickson.net/a-better-and-easier-grid-loop/ –delete this comment and I can repost my question over there.
Jason says
Hey Bill,
I have set up a custom post type and taxonomy for my site but when I go to the post type page and the custom taxonomy base page they are completely blank. Not a 404, just blank. I have created an archive template, archive-kellen_clients.php and I am using your template selection function but still nothing. I have re-saved the permalink structure but this didn’t help either.
Custom Post Type Page
http://kellenco.wpengine.com/clients/
Custom Taxonomy – for a category that I created
http://kellenco.wpengine.com/client-category/testing/
The post itself shows up http://kellenco.wpengine.com/clients/testing/
Here is my entry from my functions.php file.
https://gist.github.com/jasonabney/0fe2d314937d3c10f976
Any clue what I am doing wrong? I am hoping that you have seen this before. Thank you for your help and a huge thank you for all of your tutorials.
Bill Erickson says
It looks like you have a php error that’s preventing the page from loading.
In wp-config.php, add
define( 'WP_DEBUG', true );
to turn on debug mode.Once debug mode is on, reload those pages and you should get an error message that points you in the right direction.
Jason says
Thanks Bill. Unfortunately it is still a completely blank page. define( ‘WP_DEBUG’, true ); didnt’ show anything on localhost or on the server.
Jamie Mitchell says
Can you post your code on github and add the link here, then i can take a look for you.
Jason says
Hey Jamie,
My full functions.php file is here. https://gist.github.com/jasonabney/3ac206df0a00e370498e
I’ll keep adding files there. If there is anything else you want to see let me know. Thanks for taking the time to look at this.
Jason says
Bill-Jamie,
When in doubt… start over. I created a new WordPress install and moved functions in one at a time.
This is the function that created the problem.
add_filter( ‘template_include’, ‘be_template_chooser’ );
https://gist.github.com/jasonabney/3ac206df0a00e370498e#file-function-template-chooser