Block Hooks & Events
When to Use
Reacting to block operations or altering block behavior across all blocks or specific plugins.
Items
hook_block_view_alter
Description: Modify block render array after build() but before rendering
Signature:
function hook_block_view_alter(
array &$build,
\Drupal\Core\Block\BlockPluginInterface $block
) {}
function mymodule_block_view_alter(&$build, BlockPluginInterface $block) {
if ($block->getPluginId() === 'system_branding_block') {
$build['#attached']['library'][] = 'mymodule/branding-enhancements';
$build['#cache']['tags'][] = 'config:mymodule.branding';
}
}
$build is the render array from build(); don't replace, modify; preserve cache metadata
hook_block_access
Description: Control access to block viewing for all blocks Signature:
function hook_block_access(
\Drupal\block\Entity\Block $block,
$operation,
\Drupal\Core\Session\AccountInterface $account
) {}
function mymodule_block_access(Block $block, $operation, AccountInterface $account) {
if ($operation === 'view' && $block->getPluginId() === 'my_block') {
return AccessResult::forbiddenIf(!$account->hasPermission('view my block'))
->addCacheContexts(['user.permissions']);
}
return AccessResult::neutral();
}
AccessResult, not boolean; use neutral() when not making a decision
hook_block_view_BASE_BLOCK_ID_alter
Description: Alter specific block plugin by ID Signature:
function hook_block_view_BASE_BLOCK_ID_alter(
array &$build,
\Drupal\Core\Block\BlockPluginInterface $block
) {}
function mymodule_block_view_system_branding_block_alter(&$build, BlockPluginInterface $block) {
// Only affects system_branding_block
$build['#prefix'] = '<div class="custom-branding-wrapper">';
$build['#suffix'] = '</div>';
}
BASE_BLOCK_ID with plugin ID, underscores only (replace hyphens/colons)
hook_block_build_alter
Description: Legacy hook, deprecated in favor of hook_block_view_alter
Status: Deprecated
Gotchas: Don't use; use hook_block_view_alter instead
hook_entity_view_alter (for Block config entity)
Description: Alter Block config entity rendering (rare use case) Signature:
function hook_entity_view_alter(
array &$build,
\Drupal\Core\Entity\EntityInterface $entity,
\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
) {}
function mymodule_entity_view_alter(&$build, EntityInterface $entity, $display) {
if ($entity->getEntityTypeId() === 'block') {
// Rarely needed; blocks rendered via BlockViewBuilder, not entity view
}
}
hook_block_view_alter instead
preprocess_block
Description: Theme preprocess for block template variables Signature:
function hook_preprocess_block(&$variables) {}
function mytheme_preprocess_block(&$variables) {
$block = $variables['elements']['#block'];
$plugin_id = $block->getPluginId();
$variables['custom_class'] = 'block-' . str_replace('_', '-', $plugin_id);
$variables['region'] = $block->getRegion();
}
$variables['elements']['#block']; don't modify $variables['content'] structure
Common Mistakes
- Using
hook_block_build_alter→ Deprecated; usehook_block_view_alter - Returning wrong type from
hook_block_access→ Must returnAccessResult, not boolean or NULL - Replacing
$buildarray in alter hooks → Modify existing array; preserve cache metadata - Not adding cache contexts when access varies → Leads to incorrect caching and wrong content shown
- Using generic
hook_block_view_alterwhenhook_block_view_BASE_BLOCK_ID_alteris more appropriate → Specific hook is more efficient
See Also
- Block Access Control
- Block Rendering & Theming
- Reference: https://api.drupal.org/api/drupal/core%21modules%21block%21block.api.php/group/block_api