I've done this previously but I've forgotten the name of the hook, and can't find it anywhere...

What I'm trying to do is add some custom columns in the listing of a custom post type in the admin.

For example, in the admin, click on articles, I want to add custom column there.


6 Answers 6


The hooks to create custom columns and their associated data for a custom post type are manage_{$post_type}_posts_columns and manage_{$post_type}_posts_custom_column respectively, where {$post_type} is the name of the custom post type.

This example from the documentation removes the author column and adds a taxonomy and meta data column:

// Add the custom columns to the book post type:
add_filter( 'manage_book_posts_columns', 'set_custom_edit_book_columns' );
function set_custom_edit_book_columns($columns) {
    unset( $columns['author'] );
    $columns['book_author'] = __( 'Author', 'your_text_domain' );
    $columns['publisher'] = __( 'Publisher', 'your_text_domain' );

    return $columns;

// Add the data to the custom columns for the book post type:
add_action( 'manage_book_posts_custom_column' , 'custom_book_column', 10, 2 );
function custom_book_column( $column, $post_id ) {
    switch ( $column ) {

        case 'book_author' :
            $terms = get_the_term_list( $post_id , 'book_author' , '' , ',' , '' );
            if ( is_string( $terms ) )
                echo $terms;
                _e( 'Unable to get author(s)', 'your_text_domain' );

        case 'publisher' :
            echo get_post_meta( $post_id , 'publisher' , true ); 

  • 2
    Thank you for this hook!!! This is brilliant!! There's no clear documentation on stackoverflow!!! :D Cheers mate Commented May 30, 2019 at 2:12
  • isn't there any option to set the column number? like column_index[2]. Because the custom_column appears at the end of the column. Commented Jun 21, 2019 at 8:52
  • 2
    @DilipGupta $columns is an array, you can reorder before you return it
    – fogx
    Commented Jul 16, 2019 at 10:39
  • 1
    In the case of adding a taxonomy term column. How would you add a link below the term name that would Filter the table to that term? I have seen this on a few plugins before.
    – iamonstage
    Commented Mar 14, 2021 at 15:01
  • @iamonstage register_taxonomy() now has support for that built in via the show_admin_column setting passed via arguments. developer.wordpress.org/reference/functions/register_taxonomy. If you aren't registering the taxonomy yourself, you can still alter the args via the register_taxonomy_args filter developer.wordpress.org/reference/hooks/register_taxonomy_args Commented Mar 15, 2021 at 6:05

I wrote a function that combines manage_{$post_type}_posts_columns filter and manage_{$post_type}_posts_custom_column action.

EDIT: Added column sorting filter manage_edit-{$post_type}_sortable_columns and pre_get_posts action.

function add_admin_column( $column_title, $post_type, $cb, $order_by = false, $order_by_field_is_meta = false ){

    // Column Header
    add_filter( 'manage_' . $post_type . '_posts_columns', function( $columns ) use ($column_title) {
        $columns[ sanitize_title($column_title) ] = $column_title;
        return $columns;
    } );

    // Column Content
    add_action( 'manage_' . $post_type . '_posts_custom_column' , function( $column, $post_id ) use ($column_title, $cb) {
        if( sanitize_title($column_title) === $column)
    }, 10, 2 );

    // OrderBy Set?
    if( !empty( $order_by ) ) {

      // Column Sorting
      add_filter( 'manage_edit-' . $post_type . '_sortable_columns', function ( $columns ) use ($column_title, $order_by) {
          $columns[ sanitize_title($column_title) ] = $order_by;
          return $columns;
      } );

      // Column Ordering
      add_action( 'pre_get_posts', function ( $query ) use ($order_by, $order_by_field_is_meta) {
          if( ! is_admin() || ! $query->is_main_query() )

          if ( sanitize_key($order_by) === $query->get( 'orderby') ) {
                  $query->set( 'orderby', 'meta_value' );
                  $query->set( 'meta_key', sanitize_key($order_by) );
              else {
                  $query->set( 'orderby', sanitize_key($order_by) );
      } );



add_admin_column(__('EAN'), 'product', function($post_id){
    echo get_post_meta( $post_id , 'ean' , true ); 
}, 'meta_key_name', true);

The meta_key_name can be omitted to avoid sorting. Otherwise, you will need to know your exact meta_key name for sorting to work properly.

add_admin_column(__('Last Modified'), 'post', function($post_id){
    echo get_the_modified_date(); 
}, 'modified');

In this case, modified isn't a meta key.

  • Works like a charm, thank you. How can I manage to get the newly created column sortable? Commented Jan 7, 2021 at 11:02
  • Try this article in Smashing Magazine smashingmagazine.com/2017/12/… Commented Mar 3, 2021 at 17:45
  • Doing these things is where you find the wordpress POWER... Thank you!
    – gtamborero
    Commented Jan 21, 2023 at 17:33

I'm not sure if it default custom meta data that you want to show as columns, but you could consider to use this free plugin that allows you to add columns to display custom fields. https://wordpress.org/plugins/codepress-admin-columns/

The pro version even allows you to add filtering, sorting and inline edit to those columns.

  • DO NOT USE! Will break your site: PHP Fatal error: Cannot redeclare AC() (previously declared in [redacted]\\wp-content\\themes\[redacted]\\functions.php:628) in [redacted]\\wp-content\\plugins\\codepress-admin-columns\\api.php on line 9 Commented Nov 6, 2018 at 20:17
  • @PeterKionga-Kamau this is a compatibility-issue with your specific theme. The error you posted will not appear with a default WP theme (such as twentytwenty); I also use the plugin with the Divi Theme and find it quite useful
    – Philipp
    Commented Oct 8, 2019 at 19:34
  • Isn't there some kind of namespacing or use of less generic function names that could be used to avoid this? Commented Oct 9, 2019 at 20:06

Based on Carlos response (thank you very much) I wanted to add a little thumbnail image, but not a featured one, one from custom metabox.

So this is what I added:

add_admin_column(__('Thumbnail'), 'obraz', function($post_id){
$image_id =  get_post_meta( $post_id , 'custom_thumbnail_metabox' , true );
echo '<img src="'.wp_get_attachment_image_url($image_id).'" />';

Info: the post meta will get the ID of the attachment/image so thats the reason for later call of wp_get_attachment_image_url


This guide worked for me. In my case, I'm using CPT UI to create the custom post types, ACF to create the custom post fields, and Code Snippets to glue everything together.

I've included a simplified version of the code below.

Change custom-post-type-slug, custom_post_type_slug, and custom_column_name below to suit your needs. Be sure to maintain the same dash/underscore format when changing the names.

Note that get_field() is specific to the ACF plugin. If you're not using ACF, you may want to use the built-in get_post_meta() WordPress function to retrieve and display post metadata.

add_filter('manage_custom-post-type-slug_posts_columns', 'set_custom_edit_custom_post_type_slug_columns');

function set_custom_edit_custom_post_type_slug_columns($columns) {
    $columns['custom_column_name'] = 'Custom Column Title';
    return $columns;

add_action('manage_custom-post-type-slug_posts_custom_column' , 'custom_custom_post_type_slug_column', 10, 2);

function custom_custom_post_type_slug_column($column, $post_id) {
    switch ($column) {
        case 'custom_column_name':
            echo get_field('custom_column_name', $post_id);

add_filter('manage_edit-custom-post-type-slug_sortable_columns', 'set_custom_custom_post_type_slug_sortable_columns');

function set_custom_custom_post_type_slug_sortable_columns($columns) {
    $columns['custom_column_name'] = 'custom_column_name';
    return $columns;

add_action('pre_get_posts', 'custom_post_type_slug_custom_orderby');

function custom_post_type_slug_custom_orderby($query) {
    if ( ! is_admin()) {

    $orderby = $query->get('orderby');

    if ('custom_column_name' == $orderby) {
        $query->set('meta_key', 'custom_column_name');
        $query->set('orderby', 'meta_value_num');

For woocommerce orders I needed to use the filter: manage_edit-shop_order_columns.

add_filter( 'manage_edit-shop_order_columns', 'manage_shop_order_posts_columns', 10, 1 );
function manage_shop_order_posts_columns($columns) {
    $columns['test'] = 'Test';
    return $columns;

Then to populate the field:

add_action( 'manage_shop_order_posts_custom_column' , 'manage_shop_order_custom_column', 10, 2 );
function manage_shop_order_custom_column($column, $post_id) {
    switch ( $column )
        case 'test' :
            echo '1234';

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.