Action Hook since 2.3.0

transition_post_status

Fires whenever a post's status changes

Description

transition_post_status is the only hook that reliably tells you a status changed — including the old value — which save_post cannot do.

When it runs

Inside wp_insert_post()/wp_transition_post_status(), before the more specific {$new_status}_{$post_type} hooks.

Signature

do_action( 'transition_post_status', string $new_status, string $old_status, WP_Post $post );

Parameters

  • $new_status string — New post status.
  • $old_status string — Previous post status.
  • $post WP_Post — Post object.

Examples

Basic

add_action( 'transition_post_status', function( $new_status, $old_status, $post ) {
    if ( 'publish' === $new_status && 'publish' !== $old_status ) {
        wp_mail( get_option( 'admin_email' ), 'Post published', get_the_title( $post ) );
    }
}, 10, 3 );

Send a notification only on the actual draft-to-publish transition.

Common Use Cases

  • Detect draft-to-publish transitions
  • Log status change history
  • Trigger workflows on unpublish/trash
  • Build scheduled-publish notifications

Common mistakes

  • Using save_post to detect 'just published' — it fires on every save with no way to see the previous status

Related hooks

FAQ

How do I detect only the first publish, not every re-save?

Check that $new_status === 'publish' && $old_status !== 'publish' inside transition_post_status.

Source: wp-includes/post.php

Group wizard

One session: this hook, then each related_hooks entry. Walk it and tell us what to change.

transition_post_status → save_post

functions.php — transition_post_status
functions.php
PHP transition_post_status · Hook 1 of 2 · Step 1 of 6

Practice the transition_post_status WordPress action hook in our interactive sandbox — a VS Code-style editor with a step-by-step transition_post_status tutorial, add_action exercises, and instant feedback. No local WordPress install required.