Advanced WordPress Hooks: Actions and Filters for Customization

Arafat Islam Oct 8, 2026 4 min read
Advanced WordPress Hooks: Actions and Filters for Customization

Advanced WordPress Hooks: Actions and Filters for Customization

WordPress hooks (actions and filters) are the foundation of WordPress extensibility. Instead of modifying core files (bad practice), hooks let you customize WordPress without touching the code.

Mastering hooks transforms you from a plugin-dependent user to an independent WordPress developer.

What Are Hooks?

Hooks are points in WordPress where you can inject custom code.

Two types:

Actions: Do something at a specific point

  • Example: "When post is published, send notification email"

Filters: Modify data before it's used

  • Example: "Before displaying post title, add emoji prefix"

Understanding Actions

Actions are like events. WordPress triggers actions at specific moments.

Common actions:

  • wp_head - In page
  • wp_footer - In page footer
  • wp_enqueue_scripts - Load CSS/JavaScript
  • init - WordPress initialization
  • save_post - Post saved
  • user_register - User registers

Using actions:

add_action('init', function() {
  echo "WordPress has initialized!";
});

Real example:

// Send email when post published
add_action('publish_post', function($post_id) {
  $post = get_post($post_id);
  wp_mail(
    get_option('admin_email'),
    'New post published: ' . $post->post_title,
    'Check it out: ' . get_permalink($post_id)
  );
});

Understanding Filters

Filters modify data. WordPress passes data through filters before displaying.

Common filters:

  • the_content - Post content before display
  • the_title - Post title before display
  • post_type_archive_title - Archive page title
  • wp_mail - Email data before sending
  • option_siteurl - Site URL before use

Using filters:

add_filter('the_title', function($title) {
  return '✨ ' . $title . ' ✨';
});

This adds emoji before and after every post title.

Real example:

// Add affiliate link to post content
add_filter('the_content', function($content) {
  if (is_single()) { // Only on single posts
    $affiliate_link = '<p><a href="https://amazon.com/?tag=myaffiliate">View on Amazon</a></p>';
    return $content . $affiliate_link;
  }
  return $content;
});

Actions vs Filters: Key Difference

Actions:

  • Execute code at a point
  • Don't modify data
  • Used for side effects (sending email, logging, etc.)
  • Syntax: add_action()

Filters:

  • Receive data, return modified data
  • Must return something
  • Used for transformations
  • Syntax: add_filter()

Example distinguishing:

// ACTION: Do something
add_action('wp_head', function() {
  echo '<meta name="custom" content="value">';
  // Doesn't return anything
});

// FILTER: Transform data
add_filter('the_title', function($title) {
  return strtoupper($title); // MUST return value
});

Priority and Arguments

Hooks support priority (execution order) and arguments (data passed to function).

Priority:

  • Default: 10
  • Lower number = earlier execution
  • Higher number = later execution
// These run in order: 5, 10, 15
add_filter('the_title', function($title) { return 'First: ' . $title; }, 5);
add_filter('the_title', function($title) { return 'Second: ' . $title; });
add_filter('the_title', function($title) { return 'Third: ' . $title; }, 15);

Arguments: Some hooks pass multiple parameters.

// save_post passes $post_id and $post object
add_action('save_post', function($post_id, $post) {
  echo 'Post ID: ' . $post_id;
  echo 'Post title: ' . $post->post_title;
}, 10, 2); // "2" = accept 2 arguments

Custom Hooks

Create your own hooks for plugin/theme extensibility.

Custom action:

// In your plugin/theme
do_action('my_custom_event', $data);

// In functions.php or another plugin
add_action('my_custom_event', function($data) {
  echo 'Someone triggered my hook!';
});

Custom filter:

// In your plugin/theme
$content = apply_filters('my_custom_filter', $content);

// In functions.php
add_filter('my_custom_filter', function($content) {
  return '✨ ' . $content . ' ✨';
});

Common Use Cases

Adding JavaScript

add_action('wp_enqueue_scripts', function() {
  wp_enqueue_script(
    'my-script',
    get_template_directory_uri() . '/js/custom.js',
    array('jquery')
  );
});

Adding CSS

add_action('wp_enqueue_scripts', function() {
  wp_enqueue_style(
    'my-style',
    get_template_directory_uri() . '/css/custom.css'
  );
});

Modifying Post Content

add_filter('the_content', function($content) {
  if (is_single()) {
    $content .= '<div class="disclaimer">This is a disclaimer</div>';
  }
  return $content;
});

Adding Admin Notices

add_action('admin_notices', function() {
  echo '<div class="notice notice-warning"><p>Custom warning message</p></div>';
});

Scheduled Tasks

// Register hook
add_action('my_hourly_event', 'do_something_hourly');

// Schedule it
if (!wp_next_scheduled('my_hourly_event')) {
  wp_schedule_event(time(), 'hourly', 'my_hourly_event');
}

// Function to run
function do_something_hourly() {
  // Your code runs hourly
}

Where to Add Hooks

Option 1: functions.php

// In your theme or child theme
// wp-content/themes/your-theme/functions.php
add_filter('the_title', function($title) {
  return strtoupper($title);
});

Pros: Simple, no plugin needed Cons: Lost if theme changed, not best practice

Option 2: Must-Use Plugin

// Create file: wp-content/mu-plugins/custom.php
// mu-plugins always load first
add_filter('the_title', function($title) {
  return strtoupper($title);
});

Pros: Always loads, can't be deactivated Cons: Limited visibility

Option 3: Custom Plugin

// Create file: wp-content/plugins/my-hooks/my-hooks.php
<?php
/**
 * Plugin Name: My Hooks
 * Description: Custom hooks
 */

add_filter('the_title', function($title) {
  return strtoupper($title);
});

Pros: Professional, easily managed, can be deactivated Cons: Requires plugin structure

Best practice: Custom plugin for extensibility.

Removing Hooks

Sometimes you need to remove hooks added by plugins/themes.

// Remove action
remove_action('wp_head', 'wp_generator');

// Remove filter
remove_filter('the_content', 'wpautop');

// Remove with priority
remove_action('wp_footer', 'function_name', 15);

Debugging Hooks

See which hooks are running and in what order.

Method 1: Query Monitor (plugin)

  • Install "Query Monitor"
  • Hooks tab shows all running hooks
  • See priority and execution order

Method 2: Manual debug

add_action('wp_head', function() {
  global $wp_actions;
  echo '<pre>';
  print_r($wp_actions);
  echo '</pre>';
});

Method 3: Log to file

add_action('init', function() {
  error_log('WordPress initialized');
});

Check wp-content/debug.log.

Best Practices

1. Use unique hook names

// Good: Plugin-specific prefix
add_action('my_plugin_init', 'do_something');

// Bad: Too generic
add_action('init', 'do_something');

2. Document your hooks

/**
 * Action: my_plugin_init
 * Fires when my plugin initializes.
 *
 * @param string $plugin_name The plugin name
 */
do_action('my_plugin_init', 'My Plugin');

3. Set proper priority

// If you need this to run early, use low priority
add_action('wp_head', 'critical_function', 5);

// Non-critical can be default or later
add_action('wp_head', 'non_critical', 15);

4. Use anonymous functions for simplicity

// Simple hooks can use anonymous functions
add_filter('the_title', function($title) {
  return strtoupper($title);
});

// Complex hooks better as named functions
add_action('wp_insert_post', 'my_plugin_handle_post_insert');
function my_plugin_handle_post_insert($post_id) {
  // Complex logic here
}

Conclusion

Hooks are WordPress's superpower. They let you customize anything without modifying core files.

Master actions and filters, and you unlock unlimited WordPress customization possibilities.


What's your favorite WordPress hook? What customizations have you built? Share your examples in the comments.