The WordPress post ID is already in every row of your admin list, core just never prints it

작성자

카테고리:

← 피드로
DEV Community · Lucas Fenwick · 2026-07-20 개발(SW)

Show post and page IDs in the WordPress admin by enabling Custom Admin Columns in WP Adminify: go to WP Adminify > Productivity, turn on Custom Admin Columns, tick Show Post/Page ID Column, and save, and an ID column appears on the Posts and Pages list screens. WordPress core has never printed the ID in that list even though every row already carries it, because the list table renders each row as <tr id="post-123"> and every Edit link in the row already points at post.php?post=123, so the ID is loaded and printed into the page HTML while the only built-in way to read it is hovering a link and checking the browser status bar one post at a time.

That is the whole answer. The rest of this post is why the hover trick is evidence, why your IDs look random, the code route, and the migration warning nobody puts in these tutorials.

The workaround is the proof

Every guide on this topic teaches the same thing. Hover Edit, read post.php?post=123 off the status bar. Or open the post and read the ID out of the address bar.

Both work. Both also quietly admit that the ID is already rendered. Look at what WP_Posts_List_Table actually emits for one row:

<tr id="post-123"
    class="iedit author-self level-0 post-123 type-page status-publish hentry">
  <td class="title column-title">
    <a href="/wp-admin/post.php?post=123&action=edit">Sample Page</a>
    <div class="row-actions">
      <span class="edit"><a href="/wp-admin/post.php?post=123&action=edit">Edit</a></span>
      <span class="trash"><a href="/wp-admin/post.php?post=123&action=trash">Trash</a></span>
      <span class="view"><a href="/?page_id=123">View</a></span>
    </div>
  </td>
  ...
</tr>

Enter fullscreen mode Exit fullscreen mode

The ID is the row’s HTML id. It repeats in the row classes, courtesy of get_post_class(). It appears in four URLs. On a 20-row screen that is roughly a hundred printed copies of numbers you cannot read.

This is worth separating from the other famous admin blind spot. A missing featured image column is a genuine absence, the featured image is postmeta and the list table does not render postmeta. The post ID is the opposite, it is the primary key of the row the list table is already looping over. It is present and unprinted, because core treats the ID as a database internal rather than something a publisher needs.

Why your IDs look random

The most common follow-up question on this topic: why is my third page ID 6?

Because wp_posts has one auto_increment primary key, and everything lives in that table. Posts, pages, custom post types, attachments, revisions, autosaves, menu items, all of it draws from the same counter.

ID 1  -> "Hello world!" post
ID 2  -> "Sample Page"
ID 3  -> "Privacy Policy" (auto-draft)
ID 4  -> autosave / revision
ID 5  -> an uploaded image (attachment)
ID 6  -> "Home"

Enter fullscreen mode Exit fullscreen mode

Which is exactly what the demo shows: Home 6, Privacy Policy 3, Sample Page 2. There is no per-post-type sequence, gaps are normal, and IDs are never guessable. Anybody who has tried to work out an ID by counting rows has learned this the slow way.

What it actually costs

One ID is a five second hover. The pain is bulk.

  • An Elementor or query loop excluding twelve specific pages
  • A gallery or posts shortcode taking a comma-separated ID list
  • A post__in / post__not_in argument in WP_Query
  • A wp post get 123 run against a client site with WP-CLI
  • Two pages both titled “Home”, where the ID is the only unambiguous handle

Twelve IDs means twelve hovers and twelve hand transcriptions. And IDs are unforgiving in a specific way: a wrong digit does not error. post__not_in => [124] excludes some other post entirely and the loop renders happily. You find it when a client asks why the wrong page is showing.

The code route, and its fine print

// functions.php: ID column on posts and pages
add_filter('manage_posts_columns', 'wpa_id_column');
add_filter('manage_pages_columns', 'wpa_id_column');
function wpa_id_column($columns) {
    // insert ID right after the checkbox column
    return array_slice($columns, 0, 1, true)
         + ['wpa_id' => 'ID']
         + array_slice($columns, 1, null, true);
}

add_action('manage_posts_custom_column', 'wpa_id_value', 10, 2);
add_action('manage_pages_custom_column', 'wpa_id_value', 10, 2);
function wpa_id_value($column, $post_id) {
    if ($column === 'wpa_id') {
        echo (int) $post_id;
    }
}

Enter fullscreen mode Exit fullscreen mode

The hook reference is manage_{$post_type}_posts_columns. The fine print:

  • Posts and pages take separate filter pairs, and every custom post type wants its own if you need per-type control
  • Sorting is not included, that is a third hook (manage_edit-post_sortable_columns) plus a pre_get_posts tweak
  • The column claims whatever width it likes until you add admin CSS
  • It lives in functions.php, so it dies on theme switch unless you promote it to a must-use plugin

The checkbox route

I did this run with WP Adminify’s Productivity module. Enable Custom Admin Columns, tick Show Post/Page ID Column, save.

The caveats I will not skip. An ID column is display, not magic, it does not validate anything and it does not make an ID portable. It costs horizontal space on a screen that is already tight, Screen Options is where you claw that back. The docs describe this checkbox as being for “post and page table lists”, so I am not claiming custom post type coverage until someone verifies it on a real CPT. And the column is not documented as sortable, so treat sorting by ID as unconfirmed.

The warning that matters more than the column

A post ID is unique to one database, not to your site.

Export the content and re-import it, rebuild staging from a fresh install, migrate hosts with a content-level tool, and the same page comes back with a different ID. Every template, shortcode, exclude list and option row referencing the old number keeps running. Nothing throws. The wrong content renders.

So read IDs, use them, and prefer slugs wherever the API accepts one. Where you do hardcode, put the ID in a config constant or an option rather than scattered through templates, so the fix after a migration is one line and not a grep.

Short demo

Docs: Show Post/Page ID Column in WP Adminify

So: ID column always on, snippet it per post type, or still hovering the Edit link? And be honest, how many hardcoded IDs are in your production templates right now?

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다