A plugin that doesn't run, a function executed at the wrong time, a filter ignored. If you develop in WordPress, you've seen these issues. Often it's not the code itself but how and when it runs. We at Meteora Web see it daily in projects coming to us: slow sites, broken features, plugin conflicts. The culprit? Poorly used hooks. In this operational guide we cover what hooks are, how actions and filters work, priority management, and best practices to write code that doesn't break when updates arrive.
What's the difference between actions and filters in WordPress?
Actions and filters are the two types of WordPress hooks. Both let you "hook into" the execution flow of core, themes, or plugins. The difference is fundamental:
Action: executes custom code at a specific point in the page lifecycle. For example, wp_head, init, save_post. It does not return a value — it runs code (send emails, update data, enqueue scripts).
Filter: modifies data before it is returned or saved. It receives an argument (the data), transforms it, and returns it. Examples: the_content, wp_title, woocommerce_product_price.
Common mistake: using a filter where an action is needed (modifying data but not returning it) or vice versa. If your code doesn't produce the expected effect, check the hook type.
Practical example: action to add a meta box
// Correct action: adds a meta box to the post editor
add_action('add_meta_boxes', 'add_my_meta_box');
function add_my_meta_box() {
add_meta_box('my_id', 'My Box', 'render_my_meta_box', 'post', 'side', 'default');
}
Practical example: filter to modify content
// Correct filter: appends a notice after content
add_filter('the_content', 'append_notice_after_content');
function append_notice_after_content($content) {
if (is_single()) {
$content .= 'This article was updated on ' . get_the_modified_date() . '.
Sponsored Protocol
';
}
return $content;
}
What to do now: review your existing hooks. Each add_action and add_filter must match the correct type. If you don't return a value, use action. If you modify data, use filter.
How does priority work in WordPress hooks?
Priority determines the execution order when multiple functions are attached to the same hook. The $priority parameter is an integer, default 10. Lower numbers run first. Same priority runs in registration order.
Why does it matter? Imagine two plugins modifying the_content: one adds a shortcode, the other filters it. If the filter runs before the shortcode, the content won't be processed correctly.
Priority example
// Runs first (priority 5)
add_filter('the_content', 'high_priority_func', 5);
// Runs last (priority 20)
add_filter('the_content', 'low_priority_func', 20);
// Runs in the middle (priority 10, default)
add_filter('the_content', 'medium_priority_func');
Common error: not specifying priority when you need to run before or after another hook. If a plugin uses add_action('init', 'their_func', 10) and you use add_action('init', 'my_func'), they execute in registration order — not always predictable. Always specify priority when order matters.
Sponsored Protocol
How to find the right priority
Use a plugin like Query Monitor to see the list of functions hooked to an action and their priority. During development, you can also dump:
// Debug: print all functions hooked to 'wp_head'
global $wp_filter;
var_dump( $wp_filter['wp_head'] );
What to do now: if you're experiencing plugin or theme conflicts, check priorities. Use Query Monitor or the dump to understand who runs first and who runs after. Adjust your priorities accordingly.
What are the best practices for managing WordPress hooks without conflicts?
After years of WordPress development (over 8 real projects, clients across Italy), we've distilled rules that avoid 90% of conflicts:
1. Prefix function names
Don't use generic names like my_function. Use a unique prefix: mycompany_myfunction. This avoids collisions with other plugins or themes.
2. Don't remove hooks lightly
Removing another plugin's hook can break functionality. If you must, ensure you pass the same $priority used for registration. Example:
// Remove a plugin action (assuming priority 10)
remove_action('init', 'plugin_function', 10);
// To be safe, execute removal before the action runs
add_action('init', 'remove_plugin_hook', 0);
function remove_plugin_hook() {
remove_action('init', 'plugin_function', 10);
}
Note: removal must happen before the hook executes, often inside a hook with a very low priority (e.g., 0).
Sponsored Protocol
3. Use did_action() to avoid multiple executions
If your function should run only once, check whether the hook has already fired:
add_action('wp_enqueue_scripts', 'enqueue_my_script');
function enqueue_my_script() {
if (did_action('wp_enqueue_scripts') && !wp_script_is('my-script', 'enqueued')) {
wp_enqueue_script('my-script', get_template_directory_uri() . '/js/my.js', array(), '1.0', true);
}
}
4. Separate business logic from presentation
Use actions for backend logic (saving, emailing, updating data). Use filters to modify output. Mixing them creates hard-to-debug dependencies.
5. Document every custom hook
If you create plugins or themes, declare your own hooks with do_action() or apply_filters() and document them. Example:
/**
* Fires after saving a custom post type.
*
* @param int $post_id The post ID.
*/
do_action('mycompany_after_save', $post_id);
What to do now: review your plugin or theme code. Apply at least function prefixing and custom hook documentation. If working in a team, establish a common naming convention.
How to test and debug WordPress hooks?
Debugging hooks is essential for understanding execution order and conflicts. Here are the tools we use daily:
Sponsored Protocol
- Query Monitor — shows all executed hooks, attached functions, and execution times. Indispensable.
- WP Debugging — enable
WP_DEBUGandWP_DEBUG_LOGin wp-config.php to log errors and notices. - Hooks Debugger — a lightweight plugin to see which hooks fire on a page.
You can also create a simple custom log:
// Custom log to trace hook execution
if (!function_exists('log_hook')) {
function log_hook($hook_name) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('Hook executed: ' . $hook_name);
}
}
}
add_action('init', function() { log_hook('init'); });
add_action('wp_head', function() { log_hook('wp_head'); });
What to do now: install Query Monitor on your development environment. Explore the "Hooks & Actions" tab for a couple of pages. You'll learn more in 10 minutes than in hours of reading.
Which mistakes to avoid with WordPress hooks?
Here are the errors we've seen most often in projects that come to us:
- Using
add_actioninside another action without priority — e.g., addingadd_action('wp_footer', ...)insidewp_head. It works, but order is not guaranteed. Better to specify priority or use dedicated hooks. - Not removing obsolete hooks — if a plugin is deactivated, its hooks remain in the database (e.g., option transients). Clean up.
- Modifying global variables inside a filter — filters must return data, not modify globals. If you need state, use a separate action.
- Using
the_contentfor heavy manipulations — if you need to heavily alter output, consider usingtemplate_includeor a template part.
What to do now: scan your theme or plugin for add_action and add_filter. Check there are no nested hooks without priority and that filters do not modify global variables.
Sponsored Protocol
What to do now
WordPress hooks are the heart of extensible development. Use them well and your code becomes modular, maintainable, and update-proof. Here are 5 concrete actions to take now:
- Check hook types — every
add_actionandadd_filtermust be the correct type. If you modify data, use filter; if you run code, use action. - Always specify priority — even if it's 10, write it. Readers understand your intentions.
- Use Query Monitor — install it and analyze the pages of your site to see hook execution order.
- Prefix function names — avoid collisions with other plugins or themes.
- Document custom hooks — your colleagues (and your future self) will thank you.
To dive deeper into advanced WordPress development with custom themes, plugins, and REST API, visit our Pillar Page on Advanced WordPress Development (note: link placeholder for English version).