WooCommerce products use WordPress’s post status system under the hood — the standard publish, draft, pending, and private statuses all work for products. WooCommerce adds its own wc-active, wc-suspended, and similar order statuses on top. When you need a custom product status beyond the defaults — “Out of Season”, “Coming Soon”, “Discontinued”, or “Requires Approval” — you must integrate at both the WordPress post status level and the WooCommerce layer: register the status with register_post_status(), add it to the WooCommerce product status list, ensure it appears in the product editor’s status dropdown, and optionally apply a colour badge in the admin product list. All four steps are required; omitting any one of them results in the status being saved correctly but not displayed or selectable in the editor UI.
Problem: You need a "Coming Soon" product status that appears in the WooCommerce product editor's status dropdown, shows a coloured badge in the admin product list, and hides the product from shop listings while still allowing direct URL access.
Solution: Register the post status with register_post_status(), add it to WooCommerce product status arrays via filters, inject it into the quick-edit and bulk-edit dropdowns, and apply a CSS badge colour via admin_head.
<?php
add_action( 'init', 'register_coming_soon_product_status' );
function register_coming_soon_product_status() {
register_post_status( 'coming-soon', [
'label' => _x( 'Coming Soon', 'WooCommerce product status', 'textdomain' ),
'public' => true, // accessible at direct URL
'exclude_from_search' => true, // hidden from search
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Coming Soon <span class="count">(%s)</span>',
'Coming Soon <span class="count">(%s)</span>',
'textdomain' ),
] );
}
// ── Add to WooCommerce product status list ─────────────────────────────
add_filter( 'wc_product_statuses', 'add_coming_soon_to_product_statuses' );
function add_coming_soon_to_product_statuses( $statuses ) {
$statuses['coming-soon'] = __( 'Coming Soon', 'textdomain' );
return $statuses;
}
// ── Show in the product editor's "Product Status" dropdown ─────────────
add_action( 'admin_footer-post.php', 'add_coming_soon_to_editor_dropdown' );
add_action( 'admin_footer-post-new.php', 'add_coming_soon_to_editor_dropdown' );
function add_coming_soon_to_editor_dropdown() {
global $post;
if ( isset( $post ) && 'product' === $post->post_type ) {
$selected = ( 'coming-soon' === $post->post_status ) ? 'selected="selected"' : '';
echo '<script>
jQuery(function($){
$("select#post_status").append(
"<option value='coming-soon' ' . $selected . '>'
. esc_js( __( "Coming Soon", "textdomain" ) )
. '</option>"
);
' . ( $selected ? '$(".misc-pub-post-status #post-status-display").text("' . esc_js( __( "Coming Soon", "textdomain" ) ) . '");' : '' ) . '
});
</script>';
}
}
// ── Hide from main shop loop (but accessible via direct URL) ───────────
add_action( 'pre_get_posts', 'hide_coming_soon_from_shop' );
function hide_coming_soon_from_shop( $query ) {
if ( is_admin() || ! $query->is_main_query() ) return;
if ( $query->is_post_type_archive( 'product' ) || $query->is_tax( 'product_cat' ) ) {
$current_statuses = (array) $query->get( 'post_status' );
$query->set( 'post_status', array_diff( $current_statuses, [ 'coming-soon' ] ) );
}
}
// ── Coloured badge in admin product list ──────────────────────────────
add_action( 'admin_head', function () {
echo '<style>
.order-status.status-coming-soon { background: #d1ecf1; color: #0c5460; }
#post-status-info .status-coming-soon { color: #0c5460; }
</style>';
} );
NOTE: WooCommerce 3.0+ also requires you to add the status to the woocommerce_valid_order_statuses_for_payment and woocommerce_valid_order_statuses_for_cancel filters if the product status should influence order processing. For products only (not orders), the filters shown above are sufficient. Always test the status with get_post_status( $product_id ) after saving to confirm WordPress is persisting the value correctly.