Add custom columns to WordPress admin posts list

The WordPress posts admin list shows title, author, categories, tags, comment count, and date by default. Those columns cover the basics but often miss the information your editors actually need at a glance. On a news site you might want to see reading time, on a WooCommerce store you need stock status, and on a real-estate site you want property price visible without opening each post individually. Clicking in and out of dozens of records per day just to check a single field costs real time and breaks the editing flow. WordPress gives you two action and filter hooks to add any column to the admin list without writing a separate plugin. The first hook, manage_post_posts_columns, registers new column headers in the table head row. The second hook, manage_post_posts_custom_column, outputs the cell content for each post row in your new column. Together they let you surface any post meta value, taxonomy term, or computed data right on the list screen. You can also make a column sortable by registering it via manage_edit-post_sortable_columns, though that requires handling the orderby parameter in a separate query filter. The column system supports images, links, colored badges, or any HTML you can safely output with PHP. This is especially useful for custom post types that store structured data in post meta fields. No plugin is needed — these hooks are part of WordPress core and available since very early versions. The example below adds a column that displays a custom meta field called reading_time next to the default columns.

Problem: You want to display a custom meta field value as a column in the WordPress posts admin list.

Solution: Add the following code to your functions.php file:

<?php
// Register the column header
add_filter( 'manage_post_posts_columns', 'ha_add_reading_time_column' );

function ha_add_reading_time_column( $columns ) {
    $columns['reading_time'] = __( 'Reading Time', 'textdomain' );
    return $columns;
}

// Output the column content
add_action( 'manage_post_posts_custom_column', 'ha_reading_time_column_content', 10, 2 );

function ha_reading_time_column_content( $column, $post_id ) {
    if ( 'reading_time' === $column ) {
        $value = get_post_meta( $post_id, 'reading_time', true );
        echo $value ? esc_html( $value ) . ' min' : '&mdash;';
    }
}

NOTE: Replace post in the hook names with your custom post type slug to target a different post type. Always escape output with esc_html() — column content is rendered directly in the browser without automatic escaping. The fallback &mdash; displays an em dash when the meta value is empty, keeping the column visually clean.