Display comment ID and parent ID in the WordPress admin by enabling Custom Admin Columns in WP Adminify: go to WP Adminify > Productivity, turn on Custom Admin Columns, tick Show "Comment ID" Column for Comment, Also show "Parent ID", and save, and an ID column and a Parent ID column appear on the Comments screen, where Parent ID 0 means a top-level comment and any other number is the comment ID that comment replies to, because WordPress core never prints either number and instead prints the parent as an author name, "In reply to A WordPress Commenter.", which identifies nothing the moment one person replies twice in the same thread, leaving comment.php?action=editcomment&c=2 in the address bar as the only built-in way to read the real number.
That is the whole answer. The rest of this post is why the name is worse than a blank, what Parent ID 0 actually means, the code route, and a core behaviour around trash that silently breaks threads.
The substitution, not the omission
Every guide on this topic teaches the same thing. Click Edit under a comment, read the number after &c=. WPBeginner does it, and so does every page that followed it.
They all stop at the comment ID. None of them touch the parent, and the reason is that core appears to have already handled the parent. Here is what WP_Comments_List_Table::column_comment() actually emits:
printf(
__( 'In reply to %s.' ),
'<a href="' . $parent_link . '">' . $name . '</a>'
);
A display name, linked to the parent comment. Which is genuinely useful for a human reading one row, and useless for anything else, because $name is not unique inside a thread. Take a support post where one person replies four times:
ID 118 In reply to A WordPress Commenter.
ID 121 In reply to A WordPress Commenter.
ID 126 In reply to A WordPress Commenter.
Three rows, one string, three different parents. The admin cannot distinguish them and neither can you.
Meanwhile the row itself is built from a WP_Comment object that already holds comment_parent, and WP_Comments_List_Table prints the comment's own ID straight into the row element:
echo "<tr id='comment-$comment->comment_ID' class='$the_comment_class'>";
and into every row action URL:
Approve -> comment.php?action=approvecomment&c=2&_wpnonce=...
Spam -> comment.php?action=spamcomment&c=2&_wpnonce=...
Trash -> comment.php?action=trashcomment&c=2&_wpnonce=...
So the comment ID is printed all over the markup, exactly like the post ID is on the Posts list. The parent ID is the interesting one, and that one is loaded and thrown away.
This is worth separating from the missing post ID column, because they fail differently. A missing column is an omission, and omissions get noticed eventually. This is a substitution. Something occupies the space where the answer belongs and reads like an answer, which is why a topic this old still has no good page written about it.
Parent ID 0 is a value, not a blank
First thing anyone asks once the column is on.
comment_parent defaults to 0, and 0 is what core writes for a top-level comment. Any other number is the comment_ID of the comment being replied to. That is the entire threading model. There is no separate tree table and no depth field on the row, just a self-referencing integer.
ID Parent ID
1 0 <- thread opener
2 1 <- reply to 1
3 1 <- another reply to 1
4 3 <- reply to 3, depth 3
Which is exactly what the demo screen shows: ID 1 / Parent 0 for the original comment, ID 2 / Parent 1 for the admin's reply. Two integer columns and you have reconstructed every thread on the page without opening a single comment. Depth is not stored anywhere, it is derived by walking the chain, which is also why Settings > Discussion caps nesting with "Enable threaded (nested) comments N levels deep" rather than storing a level.
Do not carry the post ID explanation over
If you read the post ID version of this topic you learned that posts, pages, custom post types, attachments, revisions and autosaves all draw from one auto_increment in wp_posts, which is why a clean install hands you page IDs 6, 3 and 2.
None of that applies here. Comments live in wp_comments with their own comment_ID auto_increment. Nothing else shares it.
Comment IDs still have gaps, for a completely different reason: deleted spam and purged trash consume numbers and never return them. A site running Akismet for five years can be at comment ID 40,000 with two hundred real comments on the screen. Same symptom, different cause, and the wrong explanation is currently the popular one.
What it actually costs
One comment ID is a five second click into Edit. The pain shows up when the ID is not the thing you needed.
- Auditing a thread that has been moderated for months, where "In reply to [same name]" appears on six rows
- A
parentorparent__inargument inWP_Query's comment counterpart,WP_Comment_Query -
wp comment list --fields=comment_ID,comment_parentwith WP-CLI to diff before and after a migration - Deciding what a bulk delete is about to take down with it
- A support thread where two commenters share a display name, and the ID is the only unambiguous handle anyone has
And comment IDs fail the same silent way post IDs do. A wrong digit does not error. get_comment(1263) returns null or someone else's comment and the template renders happily.
The code route, and its fine print
// functions.php: ID + Parent ID columns on the Comments screen
add_filter( 'manage_edit-comments_columns', 'wpa_comment_id_columns' );
function wpa_comment_id_columns( $columns ) {
// insert both right after the checkbox column
return array_slice( $columns, 0, 1, true )
+ [ 'wpa_cid' => 'ID', 'wpa_cpid' => 'Parent ID' ]
+ array_slice( $columns, 1, null, true );
}
add_action( 'manage_comments_custom_column', 'wpa_comment_id_values', 10, 2 );
function wpa_comment_id_values( $column, $comment_id ) {
if ( 'wpa_cid' === $column ) {
echo (int) $comment_id;
}
if ( 'wpa_cpid' === $column ) {
$comment = get_comment( $comment_id );
echo $comment ? (int) $comment->comment_parent : 0;
}
}
The hook reference is manage_comments_custom_column, and the object you are reading is documented under get_comment(). The fine print:
- The value callback receives the comment ID, not the comment object, so the Parent ID column costs a
get_comment()per row unless you prime the cache - Sorting is not included, that is a separate sortable-columns filter plus a query tweak
- Both columns claim whatever width they like 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 "Comment ID" Column for Comment, Also show "Parent ID", save.
- ID and Parent ID appear on the Comments screen, no code, survives theme switches. Note it is one checkbox for both columns, you get the pair or neither
- The same panel carries the rest of the list-screen columns people usually snippet in one at a time, Show Post/Page ID Column, Taxonomy ID, a URL Path column with per-post-type selection, and a Last Login column for users, all on the Custom Admin Columns docs page
- If you outgrow fixed checkboxes, the standalone Admin Columns Editor builds columns for arbitrary fields
- This rundown of necessary admin columns for WordPress is a decent map of which columns earn their width
- On the comment side of the same product, hiding the Comments menu from the admin panel and removing the website field from the comment form are the two adjustments most sites make first, and there is a full guide to disabling WordPress comments if the answer is that you do not want them at all
- It all sits inside the wider Productivity toolset
The caveats I will not skip. Two ID columns are display, not management. They print two numbers, they do not moderate, re-thread or repair anything, so treat "better comment management" as marketing rather than a feature. They cost two columns of horizontal space on a screen already carrying Author, Comment, In response to and Submitted on, and Screen Options is where you get that back. The docs do not document either column as sortable, so treat sorting by ID as unconfirmed. And it is a single checkbox for the pair, so there is no documented way to show ID without Parent ID.
The core behaviour that quietly breaks threads
This is the part I would put above the column itself.
wp_delete_comment() contains this, commented in core as "Move children up a level":
$children = $wpdb->get_col( $wpdb->prepare(
"SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d",
$comment->comment_ID
) );
if ( ! empty( $children ) ) {
$wpdb->update(
$wpdb->comments,
array( 'comment_parent' => $comment->comment_parent ),
array( 'comment_parent' => $comment->comment_ID )
);
}
Permanently delete a comment and its replies get re-pointed at the deleted comment's own parent. The thread survives, one level shallower. Good behaviour.
Trash does not run that block. Trashing sets the comment status and leaves comment_parent on every child exactly as it was, pointing at a comment that is no longer in the thread.
Those replies stay Approved. They render on the Comments screen looking completely ordinary. The conversation they belonged to is gone from the front end. Nothing errors, nothing is logged, and there is no admin surface that shows it, unless you can read the Parent ID column and notice a number that no longer resolves.
If you want to check a site right now:
SELECT c.comment_ID, c.comment_parent, c.comment_post_ID
FROM wp_comments c
LEFT JOIN wp_comments p ON p.comment_ID = c.comment_parent
WHERE c.comment_parent <> 0
AND ( p.comment_ID IS NULL OR p.comment_approved <> '1' );
Every row that comes back is a reply whose parent is missing or not approved.
The same logic makes the migration case worse than it is for posts. Comment IDs belong to a database, not a site. Re-import comments and they renumber, and unlike a post ID, a comment ID is referenced by other comments. If the importer does not remap comment_parent alongside comment_ID, threads flatten or, worse, reattach to whatever comment happens to hold the old number now. Run the query above after every migration.
Docs: Show Comment ID and Parent ID columns in WP Adminify
So: both columns on permanently, snippet, or still clicking Edit and reading &c=? And has anyone here actually run the orphan query on a site they inherited?
Top comments (0)