I got a bit inspired by Joost de Valk’s post on custom post type snippets. Joost provided some code snippets for adding custom columns on the edit posts page for custom post types. While the snippets are handy, I wanted to dive into this subject just a bit more.
This tutorial assumes you already know how to create custom post types and taxonomies. If not, you’ll want to check out these tutorials on post types and taxonomies.
This tutorial will walk you through creating custom columns for your post types and making these columns sortable. We’re sorting posts by two different types of data:
- Metadata: Ordering posts by meta value based on a specific meta key.
- Taxonomy: Creating links to allow the user to view all posts with a specific term.
The setup
In this tutorial, I’m using a few different things to show how the methods used work:
movie: Name of my custom post type.genre: Name of a taxonomy to group movies.duration: Post metadata key (custom field) to represent the length of the movie in minutes.
After adding a few “movies,” the edit posts screen will something look like the following screenshot.

That’s pretty bland and boring. The great thing is that WordPress provides us with the tools for making this not so boring. With a few custom functions, we can turn that page into something that looks more like the next screenshot.

Much prettier, right? We can add relevant information about the posts of our custom post type and even make the posts sortable on that screen using post metadata.
Adding custom columns
The first step in creating custom columns, is to make these columns available on the edit posts page. WordPress has a variable filter hook for this called manage_edit-{$post_type}_columns for all post types. Since the name of our post type is movie, the name of the hook you’d use is manage_edit-movie_columns.
What we’ll do here is overwrite all of the default columns and set it up however we want. In this example, we’re using five columns:
cb: The checkbox column.title: The post title column, which we’re changing to read “Movie.”duration: The column for the duration of the movie (custom metadata).genre: The column for the movie genre (custom taxonomy).date: The default post date column.
Filtering these columns is fairly simple. You only need to return an array of key/value pairs. The key is the column name and the value is the output of the column header/label.
add_filter( 'manage_edit-movie_columns', 'my_edit_movie_columns' ) ;
function my_edit_movie_columns( $columns ) {
$columns = array(
'cb' => '<input type="checkbox" />',
'title' => __( 'Movie' ),
'duration' => __( 'Duration' ),
'genre' => __( 'Genre' ),
'date' => __( 'Date' )
);
return $columns;
}
Adding content to custom columns
Now that you’ve created some custom columns, you have to actually put some content in them. We’re only worried about the duration and genre columns because they’re the only custom columns. WordPress knows how to handle the other columns by default.
The action hook in this case is manage_{$post_type}_posts_custom_column. Remember, the name of the post type is movie, so this hook becomes manage_movie_posts_custom_column.
What we’ll do is set up a PHP switch statement in our action to display our custom content when one of our custom columns is called.
- For the
durationmetadata, we’ll simply get the post meta and display its value or “Unknown” if no data exists. - For the
genretaxonomy, we’ll display the movie’s genres and add a link with some query arguments for viewing movies by genre.
I’ve tried to leave the following code fairly well commented so that you can pick it apart and use for your own projects.
add_action( 'manage_movie_posts_custom_column', 'my_manage_movie_columns', 10, 2 );
function my_manage_movie_columns( $column, $post_id ) {
global $post;
switch( $column ) {
/* If displaying the 'duration' column. */
case 'duration' :
/* Get the post meta. */
$duration = get_post_meta( $post_id, 'duration', true );
/* If no duration is found, output a default message. */
if ( empty( $duration ) )
echo __( 'Unknown' );
/* If there is a duration, append 'minutes' to the text string. */
else
printf( __( '%s minutes' ), $duration );
break;
/* If displaying the 'genre' column. */
case 'genre' :
/* Get the genres for the post. */
$terms = get_the_terms( $post_id, 'genre' );
/* If terms were found. */
if ( !empty( $terms ) ) {
$out = array();
/* Loop through each term, linking to the 'edit posts' page for the specific term. */
foreach ( $terms as $term ) {
$out[] = sprintf( '<a href="%s">%s</a>',
esc_url( add_query_arg( array( 'post_type' => $post->post_type, 'genre' => $term->slug ), 'edit.php' ) ),
esc_html( sanitize_term_field( 'name', $term->name, $term->term_id, 'genre', 'display' ) )
);
}
/* Join the terms, separating them with a comma. */
echo join( ', ', $out );
}
/* If no terms were found, output a default message. */
else {
_e( 'No Genres' );
}
break;
/* Just break out of the switch statement for everything else. */
default :
break;
}
}
At this point, you can already click on a movie genre and see all movies that have that specific genre. For example, if I click on the “Science Fiction” genre, I see a list of all the movies within that genre.

Making custom columns sortable
Now that you’ve learned how to add custom columns, it’s time to make them sortable. There’s actually two steps to this process.
- Telling WordPress that a column is sortable.
- Actually sorting the content, which we have to do since it’s custom.
The end result should look like the following screenshot.

Note that we’re only sorting by metadata here, which in our case is uses the duration meta key. The goal is to allow the user to order posts by the duration of the movie in ascending or descending order.
The first step is making the duration column sortable by filtering the manage_edit-{$post_type}_sortable_columns hook as shown in the following code.
add_filter( 'manage_edit-movie_sortable_columns', 'my_movie_sortable_columns' );
function my_movie_sortable_columns( $columns ) {
$columns['duration'] = 'duration';
return $columns;
}
The next step is where everything gets a little trickier. We need to add a filter to the request hook but only filter it when viewing this specific screen in the WordPress admin.
What we’ll do is use the load-edit.php hook to make sure we’re on the edit.php screen in the admin. If so, we’ll add our custom filter to request. From there, we’ll run a few checks to see if we need to add anything extra to the posts request.
/* Only run our customization on the 'edit.php' page in the admin. */
add_action( 'load-edit.php', 'my_edit_movie_load' );
function my_edit_movie_load() {
add_filter( 'request', 'my_sort_movies' );
}
/* Sorts the movies. */
function my_sort_movies( $vars ) {
/* Check if we're viewing the 'movie' post type. */
if ( isset( $vars['post_type'] ) && 'movie' == $vars['post_type'] ) {
/* Check if 'orderby' is set to 'duration'. */
if ( isset( $vars['orderby'] ) && 'duration' == $vars['orderby'] ) {
/* Merge the query vars with our custom variables. */
$vars = array_merge(
$vars,
array(
'meta_key' => 'duration',
'orderby' => 'meta_value_num'
)
);
}
}
return $vars;
}
The two arguments we set for the request are meta_key and orderby. We want WordPress to know that it should display posts with the duration meta key and to order them by their meta value (the actual duration of the movie).
One thing you should note is that I set the orderby to meta_value_num in this particular case. The reason for this is that the meta values are numeric. If not using numeric meta values, you should use meta_value instead.
Have fun with custom columns
Don’t go too crazy with custom columns. You should only add relevant information within your columns. This doesn’t mean add everything imaginable. Think about the relevancy of the column content and decide on whether it should be shown.
I encourage you to play around with the functions in this tutorial though. For example, try selecting a specific genre and then sorting those movies within that genre by duration. It’s pretty cool stuff.
Request:
Could you possibly expand your article on how to add a featured image/gallery iamge to the post column like the ‘movie poster’ where the post type is hierarchical?
I spent most of the weekend trying to figure that part out. There are tons of articles on how to do this with a non-hierarchical post-type. But everyone said it was virtually impossible with hierarchical, so wait until wp 3.1, which of course is out now.
Always learning something when I come to this site. What a great resource of information, thank you
It shouldn’t be any different as far as adding custom columns go. You’d just use the the_post_thumbnail() to display the featured image.
Defining a custom image size for displaying in this context might be helpful: e.g.
add_image_size( 'custom-post-list-image', X, Y, true ).Great write up! I’ve done this in many themes already, but the request and sort part still messed me up. Thanks.
Great tutorial, Justin, thanks! Finally managed to get it done as with previous tutorials there was always sth not going the way it should.
One question: For my CPT (review) I use a taxonomy of Author Rating and in the description for those ratings I use images (stars). I can show them on the frontend, but it would of course also be very cool to be able to display them in the custom columns.
I have tried to use $term->description in the line of esc_html, but that doesn’t seem to work for images (for “normal” descriptions it does work). How to accomplish this feat?
Thanks!
Piet
sorry, a premature question, found the solution myself already. As it turned out I was trying it on the wrong line…
I adapted the line that says: echo join( ‘, ‘, $out );
into: echo $term->description;
and that works perfect!
Thanks again!
Both your article and Joost’s are great – thanks. I wonder if it’d be something that either of you will look to turn into a plugin? Seems like a natural evolution given the usefulness of custom post types. Or is there a reason not to?
There’s thousands upon thousands permutations possible here, so I’m not sure if a standalone plugin would cover it. I could see something like this working on a smaller scale in a custom post type and taxonomy plugin though.
I made a plugin which allows you to manage the custom columns without any coding. You will be able to add all sorts of columns depending on the post type. You can even add Custom Fields as columns, and preset the type of field.
I have put it up on the repository for anyone to use for free. I would love some feedback on it and improve it further!
Plugin can be found here: http://wordpress.org/extend/plugins/codepress-admin-columns/
Tobias.
Nice tutorial! Question: How can I display those genre in the front end? Like the way we display categories and pages. Thanks!
Check out the get_the_term_list() function.
I have a simple question someone might be able to answer. When I setup a custom post type, My “Show on Screen” under Screen Options, does not work anymore. I try displaying more than the 20 it has and it always reverts back to the 20. Is this happening to anyone else or is it just me?
Works normally for me. I would bet that there is a Javascript conflict or error causing that though. Deactivate all of your plugins and switch to Twentyten. If that doesn’t fix it then there might be a database issue.
Thanks Patrick for the quick response. But i have figured out the problem and just in case anyone ever has this problem this is the solution.
You CAN NOT have a dash (-) in your register_post_type();
I was trying to use register_post_type(‘our-work’, array(…
and for some reason it did not like that. As soon as i changed it to ‘our_work’ or ‘work’ it works just fine.
Great write up Justin. I got stuck after playing with Joost de Valk’s ‘sortable snippet’. The columns seemed to be sortable, but really weren’t. This post helped me take the last step and make the columns sort the way I want them to sort. Only one question remaining for me. How do I change the default orderby parameter for a custom post type? Let’s say I have an even post type and I want my events to be ordered by start date by default. Any ideas on what’s the best way to do this?
I ended up using this snippet for ordering post types by a unix timestamp in a custom field.
Cool. I hadn’t thought about setting a default sort order. It’s something I’ll definitely be diving more into.
Thank you for this snippet Jan.
I modified it a little to meet my requests:
I needed my custom posts to be ordered by date by default (for a strange reason default was order by title)
I added
empty($_GET['orderby'])to the if-clause since it didn’t anymore allow me to order it by title after I added the code to my theme functions.phpJan,
To get a default sort order I used the following else to the initial check to see if the orderby is set:
if ( isset( $vars['orderby'] ) && ‘duration’ == $vars['orderby'] )
{
……
}
elseif ($vars['orderby'] == NULL )
{
/* Merge the query vars with our custom variables. */
$vars = array_merge(
$vars,
array(
‘meta_key’ => ‘duration’,
‘orderby’ => ‘meta_value’,
‘order’ => ‘DESC’
)
);
}
You wouldn’t happen to know the field name for last updated date would you so I could add it’s column as well? I had looked all over for the published date, thanks for including it!
Absolutely helpful tutorial. It’s just what I needed for my upcoming project. Added Devpress to my important bookmarks. Keep those helpful tutorials coming Justin.
Thanks!
Great writeup! Wondering how this might be adapted to front-end filter/sort controls (for example, sorting by the duration on the front-end, or filter by a certain duration time). Thanks!
Great advice!
Have you tried to make the column sortable without modifying the query vars?
I’ve just followed your steps but actually tried to sort by the new column before I added the filter to ‘request’ and it just worked, even with pagination. The column I added was from a meta data just as yours, and clicking on the header sorts them just fine.
Is it really necessary to modify the query vars then? How is it automatically guessing?
My biggest concern is that since it worked for me automatically, I tend to assume Wordpress queries all posts and then sort and paginate them manually. I mean how could it work otherwise without manually configuring the query logic?
And if so, how exactly modifying the query args avoid querying everything?
I wish I could debug it a bit further but I’m really fresh to Wordpress.
wbr,
Diogo
Superb write up.
The next obvious question is regarding order:
Is it possible to order the Genre column?
Not necessarily by clicking on the column heading “Genre” but when you click e.g “Science Fiction” you would expect to see all posts that are in taxonomy Science Fiction to be ordered first.
Is this possible?
+1 for this. I would love to know how to sort a post based on taxonomy. I realize the difficulty based on the fact that a post could potentially belong to multiple taxonomies, but that wouldn’t be the case for my current scenario.
If I want sortable the “genre” columns by name? Is taxonomy. Is possible?
I was wondering if I could get any support. I followed the tutorial to add a custom column to display the post parent for a custom post type I created. The post parent of each post displays very nicely. I used:
$var1 = get_post($post_id)->post_parent;
$var2 = get_post($var1)->post_title;
echo $var2;
to display the title of the post parent.
What I would like to do more is to make each post parent clickable so that only post with that post parent are displayed, just like you do with the “genre” column in your tutorial.
I tried something like:
$varurl = add_query_arg(array(‘post_type’ => $post->post_type, ‘post_parent’ => $var1), ‘edit.php’);
echo ‘‘.$var2.’‘;
but even though the each post parent is now clickable and the url shows something like: “/edit.php?post_type=my_type&post_parent=post_parent_ID”, when clicking on a post parent I still get all the other posts.
How can I make this work properly?
Thx,
Radu
I want to adopt you. Thanks for this post.
thank God for this article.
I’m having some issues with the custom attributes. When I click on the attribute it takes me to the edit page, but isn’t passing the post type so it loads “posts” by default, so naturally my cpt’s arn’t there.
I copied your snippet exact, changed Genre to my cpt name and its all showing great, except the clicking thing..
I figured out what I did. I didn’t have the global $post.
thank you!
How to sort taxonomy by ID and not by name?
Hi,
This is really helpful… I’ve gotten the columns to work, but I have a related question that I can’t seem to figure out.
I changed the Author field to “Client” in the columns, but how do I also change that label on the Edit page for my custom post type?
much appreciated!
Thanks for the helpful tutorial. +1 for how to order columns by taxonomy, genre in this case.
Fantastic post. I’ve got it working for one custom variable. I’ve got multiple custom field columns and am stuck on the adding in extra sortable columns in the my_sortable_columns function. How do I define multiple sortable columns?
Thanks!
Was looking for a way to do this after reading your custom post types article and adding some custom post types – thanks again Justin. Appreciate the detailed steps.
I’ve implemented this code, and the second snippet to set the default sort order, however I’d like to remove the ability to sort by title or date – ie. I want a fixed sort order, how can I take that functionality off? Thanks for an excellent tutorial!
Thx a lot ! It was really helpfull !
thanks for the tutorial! really helpful!
Is
manage_edit-{$post_type}_columnsstill the preferred hook for adding columns? I’m usingmanage_{$post_type}_posts_columns.As far as I know, but I haven’t looked at any recent changes. I’ve been using it just fine on my sites though.
Just now joining the party on this post. Thank you for the great detail and the commenting throughout your code. Just tried it on my own CPT with success!
Hi,
Thanks for this wonderful tutorial.
My question is : How can I keep the original “comment number” columns in the
manage_edit-{$post_type}_columnsrelated function?using
'comments' => __( 'Comments' )give me the comment number but without ‘graphics’….The
get_comments_number()function should do it.Thanks for the article, really helped me out.
I have a question about sorting… Is it possible to sort by two columns? I’ve got two postmeta fields, selected_date and selected_time. Displaying the content, I concatenate the two into one column (YYYY-MM-DD hh:mm). Ideally, if I’m sorting by that column, I’d like to sort it by both date and time – currently I’ve got it sorting only by date, following your instructions above.
I tried merging a second array, but that didn’t seem to do anything.
Great article, thanks. My Q: Is it possible to do a ‘double sort’ using two custom fields? Example: I have an Apartments CPT, with CF’s Listed and AptNumber. Can one order columns first by Listed, further sorted by AptNumber?
Hi, thanks for the tutorial, its really helpful and i manage to display the columns on my custom post type but i would like to know how will you be able to display selected options from an array of checkboxes. The custom field thats housing the checkboxes is called “region”. I have attempted using the codes below but in return, it showed me the word, “array” instead of the selected checkbox. Is there anyway around this? Some assistance or advice would be helpful. Cheers
Great article Justin! Great for learning some more advanced php, very clear breakdown. However, it’s funny how i actually for the first time in my life wanted to apply a facebook like for your post, and i can’t!! But thanks for the trouble anyway, and this form is so irritating, 4th attempt for posting a comment, hope you dont use it for all your sites..
I’m not sure what’s wrong with the comment form. It’s just the standard name/email/site/comment/submit form that’s on all WordPress installations.