Hooks: Actions & Filters
Hooks are the single most important concept in WordPress. They are the mechanism that lets you run your own code at specific points in WordPress's execution, or change data as it flows through the system — all without ever editing a WordPress core file. Themes, plugins, and even WordPress's own admin dashboard are built almost entirely out of hooks. If you understand hooks deeply, the rest of WordPress development becomes a matter of "which hook do I need, and what do I do with it."
Two Kinds of Hooks: Actions vs. Filters
WordPress has exactly two categories of hooks, and beginners often blur the line between them. The distinction is simple once you see it stated plainly:
| Hook type | Purpose | Returns a value? | Registered with | Fired with |
|---|---|---|---|---|
| Action | DO something at a specific point in time (log an event, send an email, output HTML, create a table) | No — the return value is ignored | add_action() | do_action() |
| Filter | MODIFY a piece of data as it passes through WordPress, then hand it back | Yes — must return a value | add_filter() | apply_filters() |
return a modified value, you're writing a filter. If it just needs to perform an action (echo something, write to the database, send a request), you're writing an action.A Full Worked Action Example
Actions let you hook into a moment in WordPress's lifecycle and run arbitrary code. A very common
use case is hooking into wp_footer, which fires right before the closing
</body> tag on every front-end page, to output a custom script tag:
<?php
// Output a custom analytics snippet just before </body>
function my_custom_footer_script() {
?>
<script>
console.log('Custom footer script loaded via wp_footer hook');
</script>
<?php
}
add_action('wp_footer', 'my_custom_footer_script');
Notice the function does not return anything — it simply performs an action (echoing markup)
when WordPress reaches the wp_footer point in its page rendering. This snippet would
typically live in your theme's functions.php or inside a plugin file.
A Full Worked Filter Example
Filters intercept a value, let you change it, and hand it back. A classic example is
the_content, which fires every time post content is displayed, letting you append
text after every post automatically:
<?php
// Append a note to the end of every post's content
function append_note_to_content($content) {
$note = '<p><em>Thanks for reading this post!</em></p>';
return $content . $note;
}
add_filter('the_content', append_note_to_content);
return statement. A filter callback that forgets to
return its value (or returns nothing) will silently wipe out the content it was supposed to modify —
this is one of the most common WordPress beginner bugs.Priority and Accepted Arguments
Both add_action() and add_filter() accept two optional parameters beyond
the hook name and callback — priority and the number of arguments your callback accepts:
<?php
add_action('init', 'my_function', 10, 1);
| Parameter | Meaning |
|---|---|
10 (priority) | Controls the order callbacks run when multiple functions are hooked to the same hook. Lower numbers run earlier. The default is 10, so passing a lower number (e.g. 5) makes your callback run before other default-priority callbacks, and a higher number (e.g. 20) makes it run later. |
1 (accepted args) | How many arguments WordPress should pass into your callback function when it fires the hook. Many hooks pass extra context (like the_content passing the content string) — if you need a second or third argument, you must raise this number, otherwise WordPress only passes the first one. |
Firing a Hook vs. Listening for a Hook
A common point of confusion for beginners is mixing up the "listen" functions with the "fire" functions. They serve opposite roles:
| Function | Role |
|---|---|
add_action() / add_filter() | Listening — "when this hook fires, please run my function." This is what you write in your theme or plugin. |
do_action() / apply_filters() | Firing — "this hook is happening right now, run everything that's listening." WordPress core calls these internally (e.g. do_action('wp_footer') lives inside WordPress's own template files), and you can call them yourself too, if you're building a plugin or theme that wants to be extensible by others. |
In other words: you almost always write add_action/add_filter calls as a
theme or plugin developer. You'd only write do_action/apply_filters
yourself if you're creating a custom hook point for other developers to extend your own code.
Some of the Most Commonly Used Hooks
There are hundreds of hooks in WordPress core, but a handful come up constantly in day-to-day development:
| Hook | Type | Fires when... |
|---|---|---|
init | Action | WordPress has finished loading but before any headers are sent — the standard place to register custom post types, taxonomies, and shortcodes |
wp_enqueue_scripts | Action | The front-end is about to load its scripts and stylesheets — the correct hook for adding your own CSS/JS (covered in the Enqueuing Scripts & Styles lesson) |
the_content | Filter | Post content is about to be displayed — lets you modify or append to it, as shown above |
save_post | Action | A post (of any post type) is created or updated — commonly used to save related custom field data |
Where Do These Hooks Live?
Hook callbacks are typically registered in your theme's functions.php file, or inside
a plugin's main PHP file. There is no special syntax needed beyond calling add_action()
or add_filter() — WordPress scans and runs whatever is registered by the time each hook
point is reached during the page request lifecycle.
functions.php or a plugin file — not something that runs live on this
page.