Customizing WordPress Post States

What are WordPress post states?

Post states are essentially just labels that show next to the title of a post or page in the WordPress admin post or page list view.

As you can see below, WordPress uses these labels to highlight certain pages or types of content.

If you have set a static homepage in WordPress, it will get the “Front Page” post state label assigned. Likewise, if you have assigned a privacy page, you will see the “Privacy Policy Page” label assigned. All posts or pages that are drafts get the “Drafts” label.

How do I add my own WordPress post states?

WordPress lets you assign your own post states or labels to specific posts or types of posts.

If you’ve ever installed Elementor, for example, you’ve probably noticed that any page that was created or edited with Elementor gets the “Elementor” post label assigned. This makes it clear to the user what pages or posts are making use of the Elementor plugin.

There are plenty of use cases where post states would make sense. For example, WooCommerce uses post states to show which pages have been assigned as your “Shop Page”, “Cart Page”, “Checkout Page”, “My Account Page”, and the “Terms and Conditions Page”.

Below is the code that WooCommerce uses to inject these custom post states.

add_filter( 'display_post_states', array( $this, 'add_display_post_states' ), 10, 2 );

 * Add a post display state for special WC pages in the page list table.
 * @param array   $post_states An array of post display states.
 * @param WP_Post $post        The current post object.
public function add_display_post_states( $post_states, $post ) {
	if ( wc_get_page_id( 'shop' ) === $post->ID ) {
		$post_states['wc_page_for_shop'] = __( 'Shop Page', 'woocommerce' );

	if ( wc_get_page_id( 'cart' ) === $post->ID ) {
		$post_states['wc_page_for_cart'] = __( 'Cart Page', 'woocommerce' );

	if ( wc_get_page_id( 'checkout' ) === $post->ID ) {
		$post_states['wc_page_for_checkout'] = __( 'Checkout Page', 'woocommerce' );

	if ( wc_get_page_id( 'myaccount' ) === $post->ID ) {
		$post_states['wc_page_for_myaccount'] = __( 'My Account Page', 'woocommerce' );

	if ( wc_get_page_id( 'terms' ) === $post->ID ) {
		$post_states['wc_page_for_terms'] = __( 'Terms and Conditions Page', 'woocommerce' );

	return $post_states;

Let’s take a look at a very simple and somewhat naive example. Let’s say that we want to add a “Sample Content” label to the “Hello World” post that exists on any new WordPress installation. The label will help new users understand that this content should be removed.

	function ( $post_states, $post ) {

		if ( 1 === $post->ID ) {
			$post_states['sample-content'] = __( 'Sample Content', 'my-plugin' );

		return $post_states;

The $post_states array contains a collection of post states that should be displayed. The value is what will show to the end-user. The key can be whatever you like but does play a special role if your key matches a filter in the list view. For example, WordPress core assigns draft as the key for the “Draft” label value. When someone filters the list of posts to only show drafts, WordPress won’t show the “Draft” label, since everything in that view is going to be a draft.

In our example, we check if the post ID matches 1, which is always the ID assigned to the “Hello World” post since it is created as part of the WordPress installation process.

Note: The naive part of this example is that someone may not delete the “Hello World” post, but rather edit it to contain different content. The problem here is that we’ve now hardcoded IDs that may correspond to actual content instead of sample content. However, the goal of this example is to keep things simple and show the minimum requirements necessary to implement this feature.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.