WordPress hooks let you extend a site without editing core files, but choosing between an action and a filter is not always obvious. This cheat sheet explains the difference, shows how priority and accepted arguments work, and provides copy-paste templates for a child theme or small site-specific plugin.
Overview
A WordPress hook is a point in the execution process where your code can connect to WordPress, a theme, or a plugin. Hooks are the foundation of many safe customization workflows because they let you change behavior without modifying the original software.
There are two main types:
- Actions run code at a particular moment. They are useful for adding output, registering functionality, loading assets, saving data, or triggering a process.
- Filters receive a value, allow your code to modify it, and then require your code to return the revised value.
A simple rule is: use an action when you want something to happen, and use a filter when you want to change something that WordPress or another component has already produced. The correct hook still depends on context, so check the hook's documentation, callback arguments, and execution timing before adding code.
For a broader introduction, see the WordPress Hooks and Filters Guide. If you are deciding where custom code should live, a child theme may suit presentation-related changes, while a small site-specific plugin is usually better for functionality that should remain active when the theme changes.
How to compare options
Action vs filter: the quick decision flow
- Are you adding a task at a particular event? Start by looking for an action. Examples include registering menus, enqueueing scripts, adding a dashboard notice, or running code after a post is saved.
- Are you changing a value that already exists? Look for a filter. Examples include changing a title, modifying an excerpt, adjusting an email subject, or altering a REST API response.
- Does the hook provide the data you need? Inspect the callback signature. A filter that only passes a string may not be suitable if you need the complete post object.
- Does timing matter? Compare the default priority and the point at which the hook fires. A callback that runs too early may not have the data you need; one that runs too late may be difficult to override.
- Does the change belong to the theme? Visual or template-specific behavior can live in a child theme. Business rules, integrations, and site-wide functionality generally belong in a custom plugin.
Priority and accepted arguments
WordPress commonly registers callbacks with a priority of 10. Lower numbers run earlier, while higher numbers run later. Priority is not a guarantee that your code will always win: another callback may return a different value, remove your callback, or run at a priority you did not anticipate. Use the smallest priority adjustment that solves the problem.
The fourth parameter in add_filter() and the fourth parameter in add_action() specifies how many arguments WordPress should pass to your callback. This number must match the arguments your function expects. If a hook provides two arguments and you register only one, your callback cannot use the second. If you register more arguments than the hook supplies, the extra parameters will not contain useful data.
| Question | Usually points to | Example direction |
|---|---|---|
| Do I need to run code during a lifecycle event? | Action | Register a menu or enqueue a stylesheet |
| Do I need to change text, markup, or data? | Filter | Modify an excerpt or title |
| Does the code need to survive a theme change? | Plugin | Store it in a site-specific plugin |
| Does the change affect only presentation? | Child theme or block theme customization | Adjust a template or theme-facing output |
| Does the hook belong to WooCommerce or ACF? | Plugin-specific hook | Confirm the plugin is active before relying on it |
Feature-by-feature breakdown
Theme and frontend hooks
Use theme-facing hooks when your goal is to add or alter frontend behavior. wp_enqueue_scripts is an action for loading styles and scripts through WordPress's enqueue system. Avoid printing stylesheet or script tags directly in a template when an enqueue is appropriate. The related CSS and JavaScript enqueueing guide covers dependencies and cache busting.
body_class is a filter that receives an array of classes. It is useful when a template needs an additional class for CSS or frontend JavaScript.
add_filter( 'body_class', function ( $classes ) {
if ( is_page_template( 'templates/landing-page.php' ) ) {
$classes[] = 'is-landing-page';
}
return $classes;
} );
the_content is another filter, but it should be used carefully. It can affect many contexts, including feeds and excerpts depending on how the callback is written. Restrict the change to the intended post type, context, or singular view.
Core actions and filters
Core hooks can help with menus, metadata, login behavior, post processing, and administrative features. For example, init is commonly used as an action to register post types, taxonomies, or rewrite-related functionality. A registration callback should be narrow and predictable; unrelated business logic makes future debugging harder.
save_post is an action that runs when a post is saved. It is not enough to check only the post ID before writing metadata. A robust callback should also consider autosaves, revisions, permissions, the relevant post type, and nonce validation when the save originates from a form.
For administrative changes, hooks such as admin_init and admin_enqueue_scripts can provide better scope than a general frontend hook. Check whether the current screen or user capability matches the intended target before adding settings or loading admin assets.
REST API hooks
REST customization may involve registering a route with the rest_api_init action or modifying data returned by an existing route through a route-specific filter. If you expose or alter data, treat permission checks and input validation as required parts of the implementation, not optional enhancements.
add_action( 'rest_api_init', function () {
register_rest_route( 'site-tools/v1', '/status', array(
'methods' => 'GET',
'callback' => function () {
return array( 'ready' => true );
},
'permission_callback' => function () {
return current_user_can( 'read' );
},
) );
} );
When adapting this template, decide whether the endpoint should be public, authenticated, or restricted to a capability. Do not treat a route name as a security boundary.
WooCommerce and ACF hooks
WooCommerce and Advanced Custom Fields add their own actions and filters. Their hooks are often the cleanest way to adjust product output, checkout behavior, field settings, or custom field values, but they are also more dependent on the installed plugin and its version. Verify the hook name and callback arguments against the plugin's current documentation or source before deploying.
For example, WooCommerce customization may use an action to add content near a product summary, while a filter may adjust a product display value. ACF filters commonly modify field settings or values. Keep plugin-specific code isolated so it can be disabled and tested without affecting unrelated site functionality. See the WooCommerce customization tutorial for a larger workflow.
Reusable placement templates
For a presentation-only adjustment, a child theme's functions.php can be appropriate. Use a named function when the callback may need to be removed or inspected later.
function ccs_add_body_class( $classes ) {
$classes[] = 'site-customized';
return $classes;
}
add_filter( 'body_class', 'ccs_add_body_class', 10, 1 );
For functionality that should remain active across theme changes, create a small site-specific plugin. The opening comment identifies the file as a plugin; the guard prevents direct execution in a basic way.
<?php
/**
* Plugin Name: Site Customizations
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function ccs_register_site_features() {
// Add a focused registration or setup task here.
}
add_action( 'init', 'ccs_register_site_features', 10, 0 );
Use a project-specific prefix such as ccs_ for functions and classes. This reduces the chance of naming collisions with WordPress, a theme, or another plugin. For a complete plugin structure and release checklist, read How to Build a Maintainable Custom WordPress Plugin.
Changing priority and accepted arguments
This example runs later than the default priority and accepts two arguments. The hook must actually pass both values for the second parameter to be useful.
function ccs_adjust_value( $value, $context ) {
if ( 'product' !== $context ) {
return $value;
}
return $value . ' — updated';
}
add_filter( 'some_plugin_value', 'ccs_adjust_value', 20, 2 );
Do not copy the placeholder hook into production. Replace it with a documented hook whose signature matches the callback. If you need to run after another callback, first identify that callback's priority and test the combined result rather than increasing the number repeatedly.
Best fit by scenario
- Adding a stylesheet or script: use
wp_enqueue_scriptsor the relevant admin enqueue action, then declare dependencies and versioning deliberately. - Adding a body class: use the
body_classfilter and return the complete array. - Registering a custom post type: use an initialization action and keep registration code in a plugin if the content type should survive a theme change.
- Changing generated text: find a filter that receives the specific value. Avoid broad filters when a narrower hook exists.
- Adding content around a WooCommerce component: prefer the documented WooCommerce action before overriding a template.
- Changing a block theme's layout: first check the Site Editor, templates, style settings, and block patterns. A hook may not be the simplest or most maintainable solution. The block theme customization tutorial explains that workflow.
- Debugging a plugin conflict: reproduce the issue on staging, identify which callback changes the value, and test one priority or plugin combination at a time. Keep a rollback path.
Before deployment, check PHP syntax, enable appropriate logging in a non-production environment, and test the affected frontend, editor, administrative screen, and REST response. If a hook callback changes output, also test an empty value, an unexpected post type, a logged-out visitor, and any relevant multilingual or caching layer.
When to revisit
Return to this cheat sheet when WordPress core, your theme, or a plugin receives a major update; when a hook is documented as deprecated or changed; when a new editor or REST workflow replaces an older template technique; or when a previously reliable snippet starts producing warnings or unexpected output. Plugin-specific hooks deserve particular attention because their names, arguments, and execution timing are controlled by the plugin rather than WordPress core.
A practical quarterly review can be short:
- Search the project for each custom hook callback and confirm it still has a clear purpose.
- Check PHP compatibility and review debug logs on staging.
- Verify that callback priorities and accepted arguments still match the hooks in use.
- Test theme, plugin, core, editor, checkout, and REST behavior affected by custom code.
- Remove snippets that duplicate a native setting or are no longer needed.
- Record the hook, reason, priority, arguments, and location of every production customization.
Keep this page as a working reference, but do not treat a copied snippet as automatically safe for every site. Confirm the hook signature, scope the condition, validate user-controlled data, and test on staging before deploying. The WordPress staging update checklist can help organize that final review, while the security hardening checklist covers the wider security checks that should follow custom code and plugin changes.