What are the hooks in WordPress theme development?

WordPress is a whole package of different code components working together to provide a functional Content Management System. WordPress Core, Plugins, and Themes are all part of this package. Since some of these components update separately you do not want to edit the code directly, otherwise your changes will be overwritten whenever these components update. Thus, WordPress implemented a system of Hooks.


Action hooks allow you to inject functionality into a specific point of runtime. You can create your own plugin or theme with these action hooks so that they do not get overwritten whenever a component updates. For example, plugins_loaded is an action hook that runs after all plugins have been loaded during runtime. If you wanted to run specific functionality after all the plugins have been loaded during a page request you could say:

function example_plugins_loaded_callback() {

    // Print a message then stop page request load.
    print( 'All plugins have been loaded successfully!' );
    exit();

}
add_action( 'plugins_loaded', 'example_plugins_loaded_callback' );

Once all plugins are loaded the above message gets printed to the screen and the page request stops loading additional files. This is a simple example usage but that’s the gist of how action hooks work. You could call an API or write to the database at this point if you wanted to.

Without Action Hooks, if you wanted to print the above you would need edit the wp-settings.php file directly which would eventually be overwritten whenever WordPress updates. This would break whatever functionality you would need in place when the plugins_loaded hook runs.


Filter Hooks allow you to modify specific values generated by functions. Unlike action hooks they expect a returned value. A good example for this is the filter hook the_title which allows you to modify the_title() and get_the_title() values before they’re printed to the screen. You can view the code directly by viewing the function on trac. The function and callback is similar to action hooks:

function example_the_title_callback() {

    return 'Example Title';

}
add_filter( 'the_title', 'example_the_title_callback' );

Using the above filter hook, anywhere that the_title() is called will now print Example Title instead.


Hooks also come with priorities and number of args. You will need to look up each hook to figure out what args are passed to the hook but it goes like this:

add_filter(
    'the_title',                    // Hook Name
    'example_the_title_callback',   // Callback Function Name
    10,                             // Priority
    2                               // Number of arguments
);
  • Hook Name – Not every function has available hooks. You will need to look this up on a per-need basis using the Developer Docs.
  • Callback Function – The name of the function you want to trigger.
  • Priority – When you want the hook to run. Hooks can have multiple functions attached to them, some may not be yours ( plugins or themes ). Lower priority will run before higher priority hooks.
  • Number of Arguments – The arguments passed to the callback function.

Hooks that pass more than 1 argument need to be told to give your callback function those arguments. the_title hook passes $title and $post_id. If we want to use the $post_id argument we need to tell the add_filter() function to pass those to our callback function:

/**
 * One Argument Hook
 */
function example_the_title_callback( $title ) {

    /**
     * - By default we always get 1 argument if possible.
     * - We do not have access to $post_id at this point.
     */
    $new_title = $title . '!!!';

    return $new_title;
}
add_filter( 'the_title', 'example_the_title_callback' );

/**
 * Two Arguments Hook
 */
function example_the_title_callback( $title, $post_id ) {

    /**
     * Since we specified 2 arguments, we receive the 2 arguments that the hook provides
     */
    $post = get_post( $post_id );
    $new_title = $title . ' - ' . $post->ID;

    return $new_title;
}
add_filter( 'the_title', 'example_the_title_callback', 10, 2 );

Again, you’ll need into the Developer Docs to see what arguments the hook passes.


Finally, you as a theme or plugin developer can add in your own custom hooks which will allow other developers to add any or modify specific functionality.

Let’s say you’ve written a plugin that wraps the Post Title on the frontend in <span> HTML tags for specific styling. You can add a hook to allow other developers to add custom classes to your span like so:

/**
 * My super cool plugin
 * Add HTML span tags around titles
 * Our plugin CSS can style them cool later
 */
function supercool_title( $title ) {

    // Allow theme developers or plugin developers to add classes to our span tag.
    $classes = apply_filters(
        'supercool_title_classes',      // Our custom filter name
        array(),                        // Array of classes to pass to the filter, empty in this scenario
    );

    // Filters always return
    return sprintf( '<span class="%s">%s</span>', esc_attr( implode( ' ', $classes ) ), $title );

}
add_filter( 'the_title', 'supercool_title' );

As a theme developer I can use that plugins hook to add in my own classes if I wanted to.

/**
 * Theme Devloper modifies plugin functionality
 * Adds specific class to the super cool plugin title wrapper
 */
function my_theme_supercool_title_classes( $classe ) {

    $classes[] = 'special-title';
    return $classes;

}
add_filter( 'supercool_title_classes', 'my_theme_supercool_title_classes' );

// Output
<span class="special-title">Post Title</span>

You could do the same thing with action hooks when creating your theme or plugin. WooCommerce utilizes a lot of action hooks in their templating system allowing other plugins and specific themes to add in output. Something like:

do_action( 'before_header_hook' ); // A custom action hook created by the developer.

Simply allows you, if you want to, to inject specific code at that point. Maybe an Analytics script or an entire HTML wrapper for a utility nav that the theme previously didn’t support. The possibilities are endless!