Register a custom status with register_post_status() on the init hook, then build a separate editor control if users need to select it. Registration alone does not add a status to the WordPress Administration Screen. The example below adds an “Editor review” status for blog posts, saves it safely, and shows how to run code when a post genuinely changes status.
Register the custom status
Add the registration code to a site-specific plugin or your theme’s functions.php. A plugin is usually easier to maintain because the status remains available if you change themes. WordPress documents register_post_status() as a registration function and says, “Do not use before init.” WordPress Developer Resources: register_post_status()
<?php
function acme_register_review_status() {
register_post_status( 'editor-review', array(
'label' => _x( 'Editor review', 'post status', 'acme' ),
'label_count' => _n_noop(
'Editor review <span class="count">(%s)</span>',
'Editor reviews <span class="count">(%s)</span>',
'acme'
),
'public' => false,
'internal' => false,
'protected' => true,
'private' => false,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
) );
}
add_action( 'init', 'acme_register_review_status' );
Use a stable, short status key such as editor-review; a user note on the reference page reports a 20-character storage limit for wp_posts.post_status. The status arguments are deliberate: this example makes the status non-public and protected, excludes it from search, and includes it in the WordPress admin’s all-post and status lists. The labels use translation functions so they can be localized.
Choose visibility and admin-list behavior
A registered status can have different effects depending on its flags. Decide how the status should behave before exposing it to editors; registration does not automatically make a non-public post visible on the front end or include it in search.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
- Visibility:
public,internal,protected, andprivatedescribe the status’s visibility and intended use. The example setspublicto false,protectedto true, andinternalandprivateto false. - Search:
exclude_from_searchcontrols whether posts with this status are excluded from search. The example sets it to true. - Admin lists:
show_in_admin_all_listandshow_in_admin_status_listcontrol whether posts with the status appear in the admin’s all-post list and status list. The example enables both.
These flags are not a substitute for intentionally handling visibility in your templates and queries. If a status is non-public, do not assume a theme or custom query will show its posts to visitors.
Add a selector to the classic editor
Registering a status does not add a control to the editor. WordPress.org’s post-status documentation explicitly warns: “NOTICE: This function does NOT add the registered post status to the Administration Screen.” WordPress.org: Post Status
For the classic editor’s Publish box, use post_submitbox_misc_actions. It runs after the time and date setting in that box and receives the current post object. WordPress Developer Resources: post_submitbox_misc_actions This example limits the field to blog posts and offers only the statuses intended for this workflow:
function acme_render_status_field( $post ) {
if ( 'post' !== $post->post_type ) {
return;
}
wp_nonce_field( 'acme_status_save', 'acme_status_nonce' );
$current = get_post_status( $post );
?>
<div class="misc-pub-section">
<label for="acme_post_status">Editorial status</label>
<select id="acme_post_status" name="acme_post_status">
<option value="draft" <?php selected( $current, 'draft' ); ?>>Draft</option>
<option value="editor-review" <?php selected( $current, 'editor-review' ); ?>>Editor review</option>
<option value="publish" <?php selected( $current, 'publish' ); ?>>Published</option>
</select>
</div>
<?php
}
add_action( 'post_submitbox_misc_actions', 'acme_render_status_field' );
This is a classic-editor control, not a Gutenberg status selector. If your editors use the block editor, provide an appropriate editor interface rather than assuming this hook adds a control there.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
Save the selected status safely
When WordPress saves the post, verify the nonce, reject autosaves and revisions, check the user’s capability, and accept only statuses in an explicit allow-list. Then pass the post ID and selected status to wp_insert_post(). It can insert or update a post and accepts post_status; include the existing post’s ID when updating it. WordPress Developer Resources: wp_insert_post()
A minimal save handler illustrating the required checks is:
Rank #4
function acme_save_editorial_status( $post_id ) {
if ( ! isset( $_POST['acme_status_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['acme_status_nonce'] ) ), 'acme_status_save' ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( wp_is_post_revision( $post_id ) ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( 'post' !== get_post_type( $post_id ) || ! isset( $_POST['acme_post_status'] ) ) {
return;
}
$allowed_statuses = array( 'draft', 'editor-review', 'publish' );
$status = sanitize_key( wp_unslash( $_POST['acme_post_status'] ) );
if ( ! in_array( $status, $allowed_statuses, true ) ) {
return;
}
wp_insert_post( array(
'ID' => $post_id,
'post_status' => $status,
) );
}
add_action( 'save_post', 'acme_save_editorial_status' );
The allow-list is important: a request value should not be treated as an approved status just because it came from your own select element. The nonce, capability, autosave, revision, and post-type checks constrain when the handler acts and who can request the change. If you add other selectable statuses, add them deliberately to both the control and the allow-list.
WordPress also documents that assigning a future status requires a post date. Do not use a future status through this minimal handler without supplying an appropriate date in the update data.
Recommended Free Tools
Best Value
Run code when a post enters the status
For notifications or review-task automation, use the transition_post_status action. It passes the new status, old status, and the post object. WordPress Developer Resources: transition_post_status
function acme_status_transition( $new_status, $old_status, $post ) {
if ( 'post' !== $post->post_type || $new_status === $old_status ) {
return;
}
if ( 'editor-review' === $new_status ) {
// Notify the editorial team or enqueue a review task.
}
}
add_action( 'transition_post_status', 'acme_status_transition', 10, 3 );
The equality check prevents work from running when the status did not actually change. That distinction matters because status/post-type-specific dynamic hooks may also run on later updates where the status remains unchanged. Use the transition hook for work that should happen because a post entered a status, and make the workflow safe to retry if your notification or queueing system can run more than once.
Read or inspect a status in code
Use get_post_status( $post_id_or_object ) to retrieve the current status string; it returns false if it cannot retrieve a status. A custom status introduced by a plugin may be returned. WordPress Developer Resources: get_post_status()
Use get_post_status_object( 'editor-review' ) when you need the registered status object and its settings. It returns null if that status is not registered. WordPress Developer Resources: get_post_status_object()
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Implementation checklist
- Register the status on
initwith a stable, short key. - Set its visibility, search behavior, and admin-list flags to match the editorial workflow.
- Add an editor control separately; registration alone does not create one.
- On save, verify the nonce, autosave and revision state, editing capability, post type, and status allow-list.
- Update with
wp_insert_post()and the postID. - For transition-only side effects, compare old and new status values.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

