WordPress Post Hooks Reference
Complete reference of hooks related to posts, content, revisions, and featured images.
Post CRUD Hooks
Insertion Hooks (Create)
pre_post_insert
Fires immediately before a new post is inserted.
do_action( 'pre_post_insert', $data );
$data(array) – Unslashed post data
Since: 6.9.0
wp_insert_post_empty_content
Filters whether the post should be considered empty.
apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr );
$maybe_empty(bool) – Whether post is empty$postarr(array) – Post data
Return true to prevent insertion.
wp_insert_post_parent
Filters the post parent (for hierarchy loop prevention).
apply_filters( 'wp_insert_post_parent', $post_parent, $post_id, $new_postarr, $postarr );
wp_insert_post_data
Filters slashed post data before database insertion.
apply_filters( 'wp_insert_post_data', $data, $postarr, $unsanitized_postarr, $update );
$data(array) – Slashed, sanitized, processed post data$postarr(array) – Sanitized but unprocessed post data$unsanitized_postarr(array) – Original unsanitized data$update(bool) – Whether updating existing post
wp_insert_attachment_data
Same as above, but for attachments.
apply_filters( 'wp_insert_attachment_data', $data, $postarr, $unsanitized_postarr, $update );
wp_insert_post
Fires after a post is saved (create or update).
do_action( 'wp_insert_post', $post_id, $post, $update );
$post_id(int)$post(WP_Post)$update(bool)
save_post_{$post_type}
Fires after a post of specific type is saved.
do_action( "save_post_{$post->post_type}", $post_id, $post, $update );
Examples: save_post_post, save_post_page, save_post_movie
save_post
Fires after any post is saved.
do_action( 'save_post', $post_id, $post, $update );
Note: This fires for ALL post types including revisions. Check $update and post type if needed.
wp_after_insert_post
Fires after a post is fully inserted/updated, including terms and meta.
do_action( 'wp_after_insert_post', $post, $update, $post_before );
$post(WP_Post) – Post object after changes$update(bool)$post_before(WP_Post|null) – Post before changes (null if new)
Since: 5.6.0 – Preferred over save_post for most uses.
Update Hooks
pre_post_update
Fires before a post is updated.
do_action( 'pre_post_update', $post_id, $data );
$post_id(int)$data(array) – Unslashed post data
edit_post_{$post_type}
Fires after a specific post type is updated.
do_action( "edit_post_{$post->post_type}", $post_id, $post );
edit_post
Fires after any post is updated.
do_action( 'edit_post', $post_id, $post );
post_updated
Fires after a post is updated (with before/after comparison).
do_action( 'post_updated', $post_id, $post_after, $post_before );
Delete Hooks
pre_delete_post
Filters whether to proceed with deletion.
apply_filters( 'pre_delete_post', $check, $post, $force_delete );
Return non-null to short-circuit.
before_delete_post
Fires before a post is deleted.
do_action( 'before_delete_post', $post_id, $post );
delete_post_{$post_type}
Fires before a specific post type is deleted.
do_action( "delete_post_{$post->post_type}", $post_id, $post );
delete_post
Fires just before the post is deleted from database.
do_action( 'delete_post', $post_id, $post );
deleted_post_{$post_type}
Fires after a specific post type is deleted.
do_action( "deleted_post_{$post->post_type}", $post_id, $post );
deleted_post
Fires after a post is deleted.
do_action( 'deleted_post', $post_id, $post );
after_delete_post
Fires at the conclusion of wp_delete_post().
do_action( 'after_delete_post', $post_id, $post );
Trash Hooks
pre_trash_post
Filters whether to proceed with trashing.
apply_filters( 'pre_trash_post', $trash, $post, $previous_status );
wp_trash_post
Fires before a post is sent to trash.
do_action( 'wp_trash_post', $post_id, $previous_status );
trashed_post
Fires after a post is trashed.
do_action( 'trashed_post', $post_id, $previous_status );
pre_untrash_post
Filters whether to proceed with untrashing.
apply_filters( 'pre_untrash_post', $untrash, $post, $previous_status );
untrash_post
Fires before a post is restored from trash.
do_action( 'untrash_post', $post_id, $previous_status );
untrashed_post
Fires after a post is restored from trash.
do_action( 'untrashed_post', $post_id, $previous_status );
wp_untrash_post_status
Filters the status assigned to restored posts.
apply_filters( 'wp_untrash_post_status', $new_status, $post_id, $previous_status );
Default is ‘draft’ (since 5.6.0).
Post Status Hooks
transition_post_status
Fires when post status changes.
do_action( 'transition_post_status', $new_status, $old_status, $post );
{$old_status}to{$new_status}
Dynamic hook for specific status transitions.
do_action( "{$old_status}_to_{$new_status}", $post );
Examples:
draft_to_publishpending_to_publishpublish_to_trash
{$new_status}_{$post_type}
Fires for specific status and post type.
do_action( "{$new_status}_{$post->post_type}", $post_id, $post );
Examples:
publish_postpublish_pagedraft_movie
Content Display Hooks
the_title
Filters the post title.
apply_filters( 'the_title', $post_title, $post_id );
protected_title_format
Filters the "Protected: %s" format.
apply_filters( 'protected_title_format', $prepend, $post );
private_title_format
Filters the "Private: %s" format.
apply_filters( 'private_title_format', $prepend, $post );
the_content
Filters the post content for display.
apply_filters( 'the_content', $content );
This is a critical filter – many plugins add functionality here (embeds, shortcodes, etc.).
the_content_more_link
Filters the "read more" link.
apply_filters( 'the_content_more_link', $more_link_element, $more_link_text );
the_excerpt
Filters the displayed excerpt.
apply_filters( 'the_excerpt', $post_excerpt );
get_the_excerpt
Filters the retrieved excerpt.
apply_filters( 'get_the_excerpt', $post_excerpt, $post );
excerpt_length
Filters auto-generated excerpt length (words).
apply_filters( 'excerpt_length', $length );
Default: 55 words
excerpt_more
Filters the "read more" text for auto-excerpts.
apply_filters( 'excerpt_more', $more );
Default: […]
the_guid
Filters the post GUID.
apply_filters( 'the_guid', $post_guid, $post_id );
get_the_guid
Filters the retrieved GUID.
apply_filters( 'get_the_guid', $post_guid, $post_id );
Post Class Hooks
post_class
Filters the array of post CSS classes.
apply_filters( 'post_class', $classes, $css_class, $post_id );
$classes(string[]) – Array of class names$css_class(string[]) – Additional classes$post_id(int)
post_class_taxonomies
Filters taxonomies used for class generation.
apply_filters( 'post_class_taxonomies', $taxonomies, $post_id, $classes, $css_class );
body_class
Filters the body CSS classes.
apply_filters( 'body_class', $classes, $css_class );
Post Meta Hooks
added_post_meta
Fires after post meta is added.
do_action( 'added_post_meta', $meta_id, $post_id, $meta_key, $meta_value );
updated_post_meta
Fires after post meta is updated.
do_action( 'updated_post_meta', $meta_id, $post_id, $meta_key, $meta_value );
deleted_post_meta
Fires after post meta is deleted.
do_action( 'deleted_post_meta', $meta_ids, $post_id, $meta_key, $meta_value );
add_post_meta
Fires before adding post meta.
do_action( 'add_post_meta', $post_id, $meta_key, $meta_value );
update_post_meta
Fires before updating post meta.
do_action( 'update_post_meta', $meta_id, $post_id, $meta_key, $meta_value );
delete_post_meta
Fires before deleting post meta.
do_action( 'delete_post_meta', $meta_ids, $post_id, $meta_key, $meta_value );
Post Field Filters
edit_{$field}
Filters post field for editing.
apply_filters( "edit_{$field}", $value, $post_id );
Examples: edit_post_content, edit_post_title
{$field}_edit_pre
Filters post field before editing (prefixed fields).
apply_filters( "{$field_no_prefix}_edit_pre", $value, $post_id );
Examples: content_edit_pre, title_edit_pre
pre_{$field}
Filters post field before saving (prefixed fields).
apply_filters( "pre_{$field}", $value );
Examples: pre_post_content, pre_post_title
{$field}_save_pre
Filters post field before saving (prefixed fields, alternate).
apply_filters( "{$field_no_prefix}_save_pre", $value );
Examples: content_save_pre, title_save_pre
Featured Image Hooks
has_post_thumbnail
Filters whether post has a thumbnail.
apply_filters( 'has_post_thumbnail', $has_thumbnail, $post, $thumbnail_id );
post_thumbnail_id
Filters the thumbnail attachment ID.
apply_filters( 'post_thumbnail_id', $thumbnail_id, $post );
post_thumbnail_size
Filters the requested thumbnail size.
apply_filters( 'post_thumbnail_size', $size, $post_id );
begin_fetch_post_thumbnail_html
Fires before fetching thumbnail HTML.
do_action( 'begin_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );
end_fetch_post_thumbnail_html
Fires after fetching thumbnail HTML.
do_action( 'end_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );
post_thumbnail_html
Filters the post thumbnail HTML.
apply_filters( 'post_thumbnail_html', $html, $post_id, $post_thumbnail_id, $size, $attr );
post_thumbnail_url
Filters the post thumbnail URL.
apply_filters( 'post_thumbnail_url', $thumbnail_url, $post, $size );
the_post_thumbnail_caption
Filters the displayed thumbnail caption.
apply_filters( 'the_post_thumbnail_caption', $caption );
Revision Hooks
wp_save_post_revision_check_for_changes
Filters whether to check for changes before saving revision.
apply_filters( 'wp_save_post_revision_check_for_changes', $check_for_changes, $latest_revision, $post );
wp_save_post_revision_post_has_changed
Filters whether post has changed (for revision decision).
apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $latest_revision, $post );
_wp_post_revision_fields
Filters which fields are tracked in revisions.
apply_filters( '_wp_post_revision_fields', $fields, $post );
Default fields: post_title, post_content, post_excerpt
wp_revisions_to_keep
Filters number of revisions to keep.
apply_filters( 'wp_revisions_to_keep', $num, $post );
wp_{$post_type}_revisions_to_keep
Filters revisions per post type.
apply_filters( "wp_{$post->post_type}_revisions_to_keep", $num, $post );
_wp_put_post_revision
Fires after a revision is saved.
do_action( '_wp_put_post_revision', $revision_id, $post_id );
wp_restore_post_revision
Fires after a revision is restored.
do_action( 'wp_restore_post_revision', $post_id, $revision_id );
wp_delete_post_revision
Fires after a revision is deleted.
do_action( 'wp_delete_post_revision', $revision_id, $revision );
wp_post_revision_meta_keys
Filters meta keys to be revisioned.
apply_filters( 'wp_post_revision_meta_keys', $keys, $post_type );
Sticky Post Hooks
post_stuck
Fires after a post is made sticky.
do_action( 'post_stuck', $post_id );
post_unstuck
Fires after sticky is removed.
do_action( 'post_unstuck', $post_id );
is_sticky
Filters whether a post is sticky.
apply_filters( 'is_sticky', $is_sticky, $post_id );
Post Query Hooks
posts_where
Filters the WHERE clause of post queries.
apply_filters( 'posts_where', $where, $wp_query );
posts_join
Filters the JOIN clause.
apply_filters( 'posts_join', $join, $wp_query );
posts_orderby
Filters the ORDER BY clause.
apply_filters( 'posts_orderby', $orderby, $wp_query );
posts_clauses
Filters all query clauses at once.
apply_filters( 'posts_clauses', $clauses, $wp_query );
the_posts
Filters retrieved posts array.
apply_filters( 'the_posts', $posts, $wp_query );
Slug/Permalink Hooks
wp_unique_post_slug
Filters the unique slug before generation.
apply_filters( 'pre_wp_unique_post_slug', $override_slug, $slug, $post_id, $post_status, $post_type, $post_parent );
Return non-null to short-circuit.
wp_unique_post_slug
Filters the generated unique slug.
apply_filters( 'wp_unique_post_slug', $slug, $post_id, $post_status, $post_type, $post_parent, $original_slug );
Attachment Hooks
add_attachment
Fires after an attachment is created.
do_action( 'add_attachment', $post_id );
edit_attachment
Fires after an attachment is updated.
do_action( 'edit_attachment', $post_id );
attachment_updated
Fires after attachment update with before/after.
do_action( 'attachment_updated', $post_id, $post_after, $post_before );
delete_attachment
Fires before an attachment is deleted.
do_action( 'delete_attachment', $post_id );
deleted_attachment
Fires after an attachment is deleted.
do_action( 'deleted_attachment', $post_id );
get_attached_file
Filters the attached file path.
apply_filters( 'get_attached_file', $file, $attachment_id );
update_attached_file
Filters the file path before updating.
apply_filters( 'update_attached_file', $file, $attachment_id );
Post Count Hooks
wp_count_posts
Filters post count results.
apply_filters( 'wp_count_posts', $counts, $type, $perm );
wp_count_attachments
Filters attachment count results.
apply_filters( 'wp_count_attachments', $counts, $mime_type );
Post Format Hooks
post_formats_rewrite_base
Filters the post format rewrite base.
apply_filters( 'post_formats_rewrite_base', 'type' );
Page Hooks
wp_dropdown_pages
Filters the pages dropdown HTML.
apply_filters( 'wp_dropdown_pages', $output, $parsed_args, $pages );
wp_list_pages
Filters the pages list HTML.
apply_filters( 'wp_list_pages', $output, $parsed_args, $pages );
wp_list_pages_excludes
Filters pages to exclude from list.
apply_filters( 'wp_list_pages_excludes', $exclude_array );
Link/Pagination Hooks
wp_link_pages
Filters paginated post links HTML.
apply_filters( 'wp_link_pages', $output, $args );
wp_link_pages_args
Filters pagination arguments.
apply_filters( 'wp_link_pages_args', $parsed_args );
wp_link_pages_link
Filters individual page link.
apply_filters( 'wp_link_pages_link', $link, $i );
Password Protection Hooks
post_password_required
Filters whether password is required.
apply_filters( 'post_password_required', $required, $post );
the_password_form
Filters the password form HTML.
apply_filters( 'the_password_form', $output, $post );
Common Usage Patterns
Modify Content Before Display
add_filter( 'the_content', function( $content ) {
if ( is_single() ) {
$content .= '<div class="author-bio">...</div>';
}
return $content;
} );
Track Post Changes
add_action( 'post_updated', function( $post_id, $post_after, $post_before ) {
if ( $post_before->post_status !== $post_after->post_status ) {
// Status changed
log_status_change( $post_id, $post_before->post_status, $post_after->post_status );
}
}, 10, 3 );
Custom Post Creation Logic
add_action( 'wp_after_insert_post', function( $post, $update, $post_before ) {
if ( ! $update && $post->post_type === 'product' ) {
// New product created
setup_product_defaults( $post->ID );
}
}, 10, 3 );
Filter Revision Fields
add_filter( '_wp_post_revision_fields', function( $fields, $post ) {
if ( $post['post_type'] === 'book' ) {
$fields['isbn'] = 'ISBN'; // Track custom field
}
return $fields;
}, 10, 2 );
Conditional Featured Images
add_filter( 'post_thumbnail_html', function( $html, $post_id, $thumb_id, $size, $attr ) {
if ( empty( $html ) ) {
// Return default image if no thumbnail
return '<img src="' . get_template_directory_uri() . '/images/default.jpg" class="default-thumb" />';
}
return $html;
}, 10, 5 );
Prevent Certain Status Transitions
add_action( 'transition_post_status', function( $new, $old, $post ) {
if ( $new === 'publish' && $post->post_type === 'article' ) {
if ( ! has_post_thumbnail( $post->ID ) ) {
// Revert to draft
wp_update_post( array(
'ID' => $post->ID,
'post_status' => 'draft',
) );
wp_die( 'Articles require a featured image before publishing.' );
}
}
}, 10, 3 );