Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Hooks: actions and filters

All of WordPress is built on hooks. In hundreds of places the core announces: "scripts are about to load", "a post was saved", "here is the article text before output". Your code attaches to those points and changes the site's behaviour without touching a single core file.

Actions

An action is an event a function is tied to:

// functions.php
 
add_action('wp_footer', function () {
    echo '<p class="copyright">This site runs on WordPress</p>';
});

The function runs at the moment the theme calls wp_footer().

Filters

A filter receives a value, changes it and must return it:

// functions.php
 
add_filter('the_title', function (string $title): string {
    return strtoupper($title);
});
 
add_filter('excerpt_length', fn () => 20);

The first filter uppercases every title, the second sets the excerpt length to 20 words.

How they differ

ActionFilter
PurposeDo somethingChange something
Returns a valueNoAlways
Attached withadd_action()add_filter()
Called in the core withdo_action()apply_filters()

Priority and arguments

The third argument is the priority: the lower the number, the earlier the function runs (10 by default). The fourth is how many arguments it accepts:

add_action('save_post', function (int $postId, WP_Post $post, bool $update) {
    if ($update) {
        error_log("Post $postId changed: {$post->post_title}");
    }
}, 20, 3);

Without the fourth argument the function receives only the first parameter.

Common hooks

HookWhen it fires
after_setup_themeThe theme is loaded — time to declare its features
initWordPress is loaded — time to register post types
wp_enqueue_scriptsAdding the site's styles and scripts
admin_menuBuilding the dashboard menu
save_postA post was saved
wp_head, wp_footerOutput in head and before </body>
the_content (filter)The post text before output
pre_get_postsBefore the main posts query

Removing a handler

Someone else's function is taken off a hook by naming the same function and priority:

// functions.php
 
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'print_emoji_detection_script', 7);

The first line removes the tag with the WordPress version from the page.

Your own hooks

A theme or a plugin can create its own extension points:

<?php
// single.php
 
do_action('mytheme_before_content');
 
the_content();
 
echo esc_html(apply_filters('mytheme_read_more_text', 'Read more'));

Now other code — a child theme, for example — can add a block before the text or replace the label:

add_filter('mytheme_read_more_text', fn () => 'Details →');

Common mistake

A filter function that forgets return turns the value into null: titles, texts or links simply vanish from the page. A filter always returns the value — even when it does not need changing in this case:

add_filter('the_content', function (string $content): string {
    if (!is_single()) {
        return $content;
    }
 
    return $content . '<p>Thank you for reading!</p>';
});

Thanks for reading!

Donate to the site — we work hard for you.