Custom Post Status Generator
Generate register_post_status() code with the label, visibility flags and the admin list counts WordPress shows for a custom status.
<?php
/**
* Registers the "archived" post status.
*/
function my_plugin_register_archived_status() {
register_post_status(
'archived',
array(
'label' => _x( 'Archived', 'post status', 'my-plugin' ),
'public' => false,
'internal' => false,
'protected' => true,
'private' => false,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
/* translators: %s: number of posts. */
'label_count' => _n_noop(
'Archived <span class="count">(%s)</span>',
'Archived <span class="count">(%s)</span>',
'my-plugin'
),
)
);
}
add_action( 'init', 'my_plugin_register_archived_status' );
/**
* Adds "Archived" to the classic editor's status menu.
*
* WordPress registers the status but never adds it to that dropdown, so the
* option is appended in the browser and the current value re-selected.
*/
function my_plugin_register_archived_status_editor_option() {
$screen = get_current_screen();
$types = array_map( 'trim', explode( ',', 'post' ) );
if ( ! $screen || ! in_array( $screen->post_type, $types, true ) ) {
return;
}
$post = get_post();
$selected = ( $post && 'archived' === $post->post_status ) ? 'selected' : '';
?>
<script>
jQuery( function( $ ) {
// JSON_HEX_TAG keeps a label containing </script> from ending this block.
var label = <?php echo wp_json_encode( 'Archived', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>;
var value = <?php echo wp_json_encode( 'archived', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>;
$( 'select#post_status' ).append(
$( '<option>', { value: value, text: label, selected: <?php echo $selected ? 'true' : 'false'; ?> } )
);
<?php if ( $selected ) : ?>
$( '#post-status-display' ).text( label );
<?php endif; ?>
} );
</script>
<?php
}
add_action( 'admin_footer-post.php', 'my_plugin_register_archived_status_editor_option' );
add_action( 'admin_footer-post-new.php', 'my_plugin_register_archived_status_editor_option' );
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Register a post status of your own, with the visibility flags that decide who can read those posts and the admin counts WordPress shows beside them.
How to use
- Pick the status key. It goes in the
post_statuscolumn and is capped at 20 characters. - Set visibility. Protected keeps posts out of the front end while editors still see them; public means anyone with the URL can read them.
- Decide how the status appears in the post list: inside All, and whether it gets its own filter link with a count.
- Leave the editor dropdown option on unless you only set the status in code. WordPress registers the status but never adds it to the classic editor’s Status menu.
- Paste the result into your plugin. It hooks
initfor registration and the admin footer for the dropdown.
Example
An archive status that hides posts from the front end, keeps them out of search, and adds a filter link to the admin list:
register_post_status(
'archived',
array(
'label' => _x( 'Archived', 'post status', 'my-plugin' ),
'public' => false,
'protected' => true,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop(
'Archived <span class="count">(%s)</span>',
'Archived <span class="count">(%s)</span>',
'my-plugin'
),
)
);
label_count is the string above the list table, and it takes singular and plural forms because some languages need more than one.
Pitfalls
- The status key is capped at 20 characters and is stored on every post that uses it. Renaming it later means an UPDATE across
wp_posts. - WordPress does not add a custom status to the editor’s status dropdown. Without the appended script, the only way to set it is in code or with a bulk action.
- The block editor ignores that script entirely. It reads statuses from the REST schema, so a custom status needs editor-side JavaScript rather than an admin-footer snippet.
publicandprotectedare not opposites. Public exposes posts to the front end; protected hides them but keeps them visible to editors. Setting both leaves readers confused about which wins.- A status with
exclude_from_searchstill appears in queries that name it explicitly, includingWP_Querywithpost_statusset. - Posts left in a custom status when the plugin is deactivated stay in that status and become invisible, since nothing registers it any more. Plan the deactivation path.
show_in_admin_status_listruns a count query per status on the list screen. On a large table that is the cost of the filter link.
Compatibility
register_post_status() has been stable since WordPress 3.0 and the argument names here have not changed since. The generated dropdown snippet uses jQuery, which WordPress still ships in the admin, and applies to the classic editor only. The code targets PHP 7.0 and later; the tool runs in your browser.
Frequently asked questions
Why does my status not appear in the editor?
Does the status work with WP_Query?
post_status, either alone or in an array. It is a normal status as far as queries are concerned.