Master WordPress hooks — actions and filters — with real code examples. Learn the key differences and when to use each type in your projects.
Table of Contents
- What Are WordPress Hooks and Why They Matter
- The Two Fundamental Types: Actions and Filters
- Real Differences Between Actions and Filters
- Priorities and Arguments: Fine-Grained Control
- Custom Hooks: Create Your Own
- How to Find Available Hooks in Any Project
- Common Mistakes When Working with WordPress Hooks
- When to Use Hooks — and When Not To
What Are WordPress Hooks and Why They Matter
If you develop with WordPress beyond simply installing plugins, you’ll eventually need to modify behavior without touching the core source code, a theme, or a third-party plugin. That’s exactly what WordPress hooks are for. Understanding the different types of WordPress hooks is what separates a developer who “gets things done” from one who builds maintainable, scalable solutions.
A hook is, at its core, an attachment point. WordPress executes its code sequentially, and at key moments during that execution it fires hooks so you can “hang” your own logic onto them. This is a design pattern known as the observer pattern, widely used in software engineering. WordPress implements it in a particularly accessible way — you don’t need to master object-oriented programming to start using hooks effectively.
The WordPress core contains more than 2,500 documented hooks. WooCommerce adds several hundred more. And every theme or plugin can register its own. Knowing how they’re categorized, when they fire, and what you can do with them gives you granular control over virtually every aspect of a site.
The Two Fundamental Types of WordPress Hooks: Actions and Filters
WordPress classifies its hooks into two clear categories: action hooks and filter hooks. The difference isn’t cosmetic — it determines what kind of operation you can perform.
Action Hooks: Run Code at a Specific Moment
An action hook lets you execute a function at a specific point in the WordPress execution cycle. It doesn’t expect you to return anything. It simply says: “something is happening here — if you want to do something extra, now’s your chance.”
The function you use is add_action(). It takes at least two arguments: the name of the hook and the callback function you want to run.
Real-world example: you want to add an analytics script to the <head> of your site. You’d use the wp_head hook:
add_action( 'wp_head', 'my_analytics_script' );
function my_analytics_script() {
echo '<script>/* analytics code */</script>';
}
Some of the most commonly used action hooks in WordPress development are:
- init — Fires after WordPress has finished loading but before headers are sent. Ideal for registering custom post types and taxonomies.
- wp_enqueue_scripts — The correct place to enqueue stylesheets and scripts on the front end.
- admin_menu — Lets you add pages to the admin panel menu.
- save_post — Runs every time a post is saved. Useful for processing custom metadata.
- wp_footer — Just before the closing
</body>tag. Perfect for non-render-blocking scripts. - rest_api_init — Where you register your custom REST API endpoints.
The key with action hooks is that they don’t return a value to the main execution flow. They run side logic: inserting HTML, sending an email, writing to a log, registering a resource.
Filter Hooks: Modify Data Before It’s Used

A filter hook intercepts a piece of data, passes it to your function, and expects you to return that data — modified or not. If you forget to return anything, you break the chain and the data is lost. This is one of the most frequent mistakes developers make when they’re first learning filters.
The function is add_filter(), and the syntax is nearly identical to add_action() — but your callback must always return a value.
Example: you want to automatically append text to the end of every post:
add_filter( 'the_content', 'append_closing_text' );
function append_closing_text( $content ) {
if ( is_single() ) {
$content .= '<p>Thanks for reading.</p>';
}
return $content;
}
Essential filter hooks every WordPress developer should know:
- the_content — Filters post content before it’s displayed. One of the most powerful and versatile hooks available.
- the_title — Modifies a post’s title on the fly.
- excerpt_length — Controls the length of the automatic excerpt.
- body_class — Adds or removes CSS classes on the
<body>element. Very useful for conditional styling logic. - wp_mail_from — Changes the “from” address on emails sent by WordPress.
- login_redirect — Defines where WordPress redirects users after a successful login.
Real Differences Between Actions and Filters
Confusion between the two types of WordPress hooks is understandable, since they share syntax and internal mechanics. In fact, WordPress processes both using the same WP_Hook class internally. But the practical distinction is fundamental:
| Aspect | Action | Filter |
|---|---|---|
| Returns a value? | No | Yes (required) |
| Purpose | Execute code | Modify data |
| PHP function | add_action() | add_filter() |
| Consequence of missing return | None | Data is lost |
| Typical example | Enqueue a script | Change a text string |
A quick mental shortcut I use to decide which one I need: if the question is “do I want to do something?”, it’s an action. If the question is “do I want to change something?”, it’s a filter.
Priorities and Arguments: Fine-Grained Control
Both add_action() and add_filter() accept two additional parameters that many developers overlook: priority and number of accepted arguments.
The default priority is 10. A lower number means your function runs earlier; a higher number means it runs later. This is critical when multiple plugins or a theme hook into the same hook. If you need your filter to apply after an SEO plugin has already modified the title, you’d set a priority of 20 or higher.
add_filter( 'the_title', 'my_title_modification', 20, 2 );
The fourth parameter (2 in this example) tells WordPress how many arguments your callback accepts. Some hooks pass additional data — such as the post ID — which you’ll only receive if you declare that you’re expecting them.
According to the official WordPress developer documentation, not declaring the correct number of arguments is one of the most common causes of silent errors in plugins.
Custom WordPress Hooks: Create Your Own
You’re not limited to the hooks WordPress defines. You can create your own using do_action() and apply_filters(). This is especially relevant when you’re building plugins or themes that other developers will extend.
// In your plugin:
$price = apply_filters( 'my_plugin_final_price', $base_price, $product_id );
// Another developer can then do:
add_filter( 'my_plugin_final_price', function( $price, $id ) {
if ( $id === 42 ) {
return $price * 0.9; // 10% discount
}
return $price;
}, 10, 2 );
This extensible architecture is what makes the WordPress ecosystem so flexible. Plugins like WooCommerce expose hundreds of their own hooks precisely so you can customize behavior without ever modifying their source code.
How to Find Available Hooks in Any Project
One of the most common questions is: how do I know which hooks are available? There are several reliable strategies:
- Search the source code. Every occurrence of
do_action()orapply_filters()defines an available hook. A simple grep through the plugin or theme directory gives you the complete list. - Use a debug plugin. Tools like Query Monitor display every hook that fires on each page load, along with its associated callbacks and priorities.
- Check the code reference. The WordPress Code Reference at developer.wordpress.org documents core hooks with context and examples.
- Read the WooCommerce source. If you work with stores, WooCommerce template files are packed with
do_action()calls at strategic points — before the price, after the add-to-cart button, and so on.
Common Mistakes When Working with WordPress Hooks
After years of WordPress development, these are the errors I see come up again and again:
Forgetting the Return in Filter Hooks
I mentioned this earlier, but it deserves emphasis. Forgetting return $value; in a filter hook causes the data to vanish entirely. Post content goes blank, the title disappears, the email has no sender. And PHP won’t throw an error to alert you.
Hooking Too Early
Trying to use add_action() before WordPress has registered the target hook. For example, trying to modify the admin menu before admin_menu has fired. The fix: wrap your logic inside the correct hook.
Unplanned Priorities
When multiple plugins compete for the same hook without managing priorities, the result is unpredictable. If you’re building something others will use, document which priority your code hooks at.
Anonymous Callbacks That Can’t Be Removed
If you use anonymous functions (closures) as callbacks, you won’t be able to remove them later with remove_action() or remove_filter(). In production code — especially in plugins — always use named functions.
When to Use WordPress Hooks — and When Not To
WordPress hooks solve the vast majority of customization needs, but not everything should be done with hooks. If you need entirely new functionality that has no relationship with any existing execution point — such as a complex integration with an external system — you probably need a full plugin with its own architecture, not an add_action buried in functions.php.
It also doesn’t make sense to abuse filters to completely rewrite the HTML output of a component. If you need to radically change a template, overriding the template file is cleaner than applying a filter that replaces the entire content.
The WordPress hook system is designed for surgical modifications and modular extensions. Using it that way produces clean code. Forcing it to do everything produces spaghetti.
If you’re evaluating how to better structure a complex WordPress project, you can take a look at my WordPress development services to understand how I approach this kind of architecture.
My Take as a WordPress Developer
When I audit inherited project code, the first thing I look at is how WordPress hooks have been used. That tells me far more about the quality of the development than anything else. I’ve seen sites with dozens of functions hooked into the_content with no clear priorities, competing with each other and generating invisible bugs. And I’ve seen plugins with three well-placed filters that elegantly solved complex requirements. The difference between those two scenarios isn’t talent — it’s understanding which type of hook fits each situation and respecting its implicit contract: actions execute, filters return. It sounds simple, but truly mastering that distinction completely changes how you build on WordPress.
Need help with your project? I work with businesses and agencies on WordPress, WooCommerce, AI and integrations. Get in touch and we can discuss it.