Hook
A hook is a named extension point that lets a module add to or change Drupal's behavior without editing core or another module's code.
A hook is a named extension point in Drupal. When Drupal reaches that point, for example when a node is deleted or a form is about to be shown, it asks every enabled module whether it has an implementation, and runs each one it finds. Modules use hooks to add to or change what core and other modules do, without editing their code.
How hooks work in Drupal 11
Each hook has a name, a fixed list of parameters and a defined return value. The documentation names a hook with the prefix hook_, such as hook_form_alter. Core documents its hooks in the Hooks topic on api.drupal.org and in *.api.php files, and a module can define hooks of its own for other modules to use.
Drupal 11.1 introduced object-oriented hook implementations. You write a class in the Drupal\my_module\Hook namespace and mark a method with the Drupal\Core\Hook\Attribute\Hook attribute. The attribute takes the short hook name without the hook_ prefix. Classes in that namespace are registered as autowired services, so you can inject what you need through the constructor. The method name is free, but the parameters must match the hook.
The older procedural style still works. You put a function in the module's .module file, named after the module and the hook, so hook_form_alter in a module called my_module becomes my_module_form_alter(). Some hooks stay procedural, including install, update, schema and uninstall hooks, hook_hook_info() and hook_module_implements_alter().
Code that wants to call a hook uses the module handler service: invoke() and invokeAll() collect results, and alter() lets every module change a piece of data. A module can also be a hook author. It picks a unique name that starts with the module name, documents the hook in an .api.php file and calls it.
Order of implementations
Implementations run by module weight, lower numbers first, and modules with the same weight run alphabetically by machine name. Drupal 11.2 added an order argument on #[Hook]. The #[ReorderHook] and #[RemoveHook] attributes change the order of another module's implementation or skip it. Before 11.2 you used hook_module_implements_alter().
Example
This class adds a CSS class to the user login form. The hook is hook_form_alter, which Drupal calls for every form before it is rendered, so the method checks the form ID first.
<?php
namespace Drupal\my_module\Hook;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
class FormHooks {
#[Hook('form_alter')]
public function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
if ($form_id === 'user_login_form') {
$form['#attributes']['class'][] = 'login-form';
}
}
}
The same thing as a procedural hook is a function called my_module_form_alter() in my_module.module, with the same parameters.
Common mistakes
- Running a hook twice. A module that supports Drupal 10.1 to 11.0 and 11.1 or later keeps a small procedural function in the
.modulefile marked with#[LegacyHook]. Without that marker, Drupal 11.1 and later run both the old function and the new method. - Defining an implementation conditionally. Object-oriented hooks do not support implementations that exist only under a condition. Put the condition inside the method instead.
- Expecting an
.api.phpfile to do something. Drupal never loads these files, so copying a function from one into the wrong place changes nothing until you rename it and add it to your module. - Altering a form without checking the form ID.
hook_form_alterruns for all forms. A more specific variant,hook_form_FORM_ID_alter, runs after it for one form. - Assuming a hook runs first. Another module may implement the same hook, and weight and name decide the order.
For marketing and content teams
When a developer says a behavior "is done in a hook", they mean custom module code that reacts to something that happens on the site, such as saving or deleting content. That is useful to know when you ask for a change: a rule like "do this whenever an article is published" is a typical hook job, and it belongs in a custom module, not in the theme or in a one-off setting. Ask for it to be documented, because the code is easy to forget until the day it needs to change.
Related ideas are covered in module, Form API and cron, which has its own hook, hook_cron.