It seems like the terms hooks, filters and actions are used interchangeably. What's the difference between them? What do they do? How do I use them?

  • I found myself explaining this stuff regularly, so I wrote a simple tutorial to refer to.
    – cjbj
    May 5 '17 at 9:44

Hooks is the collective name for filters and actions. Both are meant to change the normal behaviour of functions. Programmatically speaking there is no big difference, as you can see from the fact that in the WP source code adding an action is the same as adding a filter.

The difference is in the usage. You use an action to completely change the behaviour of a function or to add something to an existing function. You use a filter to change the outcome of function or a variable. Read a bit more on the theory here.


Let's say this is your theme template file index.php:

$me = 'I love Star Wars';
$you = 'I love Star Trek';
$me = apply_filters ('do_force', $me);
$you = apply_filters ('do_force', $you);
do_action ('echo_me_you', $me, $you);

Then in the functions.php of your theme or the main php-file of your plugin you can define filters and action. For instance:

add_filter ('do_force','wpse_do_force');
function wpse_do_force ($string) {
  str_replace ('Trek','Wars', $string); // change all occurences of 'Trek ' to 'Wars'
  return $string;

The filter has no effect on $me, but it does change $you, because it searches the string for 'Trek' and changes it to 'Wars'. So, after the filters are applied we both love Star Wars.

Now, the code goes to the action. This may be very simple:

add_action ('echo_me_you', 'wpse_echo_neutral');
function wpse_echo_neutral ($me, $you) {
  echo $me;
  echo $you;

But the action can be made as powerful as you want. You could for instance nullify the filters, by changing all occurences of 'Wars' back to 'Trek':

add_action ('echo_me_you', 'wpse_echo_trek');
function wpse_echo_trek ($me, $you) {
  str_replace ('Wars','Trek', $me);
  str_replace ('Wars','Trek', $you);
  echo $me;
  echo $you;

How does this function?

When WordPress starts assembling a page, it reads your functions.php first. There it collects all the actions and filters you add. It puts these in a queue. Then, when it finds a do_action or apply_filters it takes the corresponding queue and executes the actions/filters one by one, in the order it has found them.

So, in the above example, there is one function defined that is triggered on the filter do_force and there are two functions defined that are triggered on the action echo_me_you. First, it applies the filter on $me, then it applies the filter on $you, then it executes the function wpse_echo_neutral and finally wpse_echo_trek.


After your (child) theme has been loaded, plugins (and the parent theme) get loaded as well. They add actions and filters too. So the filter you have written for the_title may seem not to work, because the filter is overruled by another filter in a plugin. This is where priorities come in handy.

Priorities allow you to determine the place of your filter/action in the queue. So if you define this:

add_filter ('the_title', 'wpse_the_title', 999, 2);

The priority 999 should make sure this filter is executed as the last one in the queue (the number 2 is the amount of parameters passed to the filter/action).

It doesn't work!

Yeah, that happens, or so it seems. As you can see from the example above, if you have an action that overrules a filter, you can do whatever with your filter and it won't have any effect. Your setup of child/parent and plugins can be so complex that you have to debug carefully.

First step is to include something like echo 'GOT HERE' as the first line of your function. This will tell you if your filter/action is called at all. If it isn't you are hooking too late. Look at this code that could be in your functions.php:

add_action ('wp_footer','wpse_footer');
function wpse_footer() {
  add_action ('wp_head', 'wpse_head');
function wpse_head() {
  echo 'GOT HERE';

This won't work, because do_action (wp_head) is already executed by WP when it encounters do_action (wp_footer). So if you add an action to the wp_head queue at that time, it has no effect. Always review the hook order when adding actions to native WordPress hooks. A regular mistake is trying to enqueue script/style files from a widget. As you can see from the hook order, dynamic_sidebar, where the widgets are hooked into the WordPress page, is executed way after wp_enqueue_scripts.

There are other reasons why a filter/action may not be executed at all. For instance, because WP skips a filter under certain circumstances. Here you have no choice but to delve into the source code (or ask here on WPSE).

Second step, once you're sure that the filter is called, is to change it to a very high priority, to make sure it isn't overruled. Also, disable all plugins to rule out interference.

Third step, if your function gets called and interference is ruled out, but it still doesn't work, it probably has something to do with your own coding skills. Check for typos, echo intermediate results, and so on to find where you went wrong.

Wrapping up

The hooks system is a powerful but sometimes confusing system to modify the behaviour of WordPress itself or plugins/themes. Because everybody is using popular hooks, the results can be unexpected. Most problems can be sorted out, however, by carefully looking at hook order and priorities.

Not the answer you're looking for? Browse other questions tagged or ask your own question.