In the transition from Drupal 7 to modern versions like Drupal 9, 10, and 11, several default theming conventions were streamlined to improve performance and maintainability. One of the most notable changes was the removal of automatic per-region block template suggestions. In Drupal 7, you could easily target a block based on where it lived; in modern Drupal, blocks are decoupled from regions by default to support the flexibility of the block system.

However, as a developer, you will frequently encounter scenarios where a block needs entirely different HTML structure depending on whether it is placed in the 'Sidebar', 'Header', or 'Footer' region. If you have been searching for a way to restore this functionality, you are in the right place. In this guide, you will learn how to use hook_theme_suggestions_block_alter to dynamically inject region-specific suggestions into your theme.

Understanding the Change in Drupal Block Theming

Modern Drupal treats blocks as standalone entities. This architectural decision was made because blocks can technically be rendered outside of the standard 'Block Layout' system—for example, via the Layout Builder, programmatically in a controller, or through a module like Page Manager.

Because a block doesn't inherently "know" its region until it is placed within a specific theme's region array, the default template suggestions only include the block's ID, the provider (module), and the bundle. To get the region, we must intercept the theming process and extract the region information from the block entity or the configuration array.

Implementing hook_theme_suggestions_block_alter

To add region-based suggestions, you need to implement a specific hook in your .theme file (if you are a theme developer) or your .module file (if you are building a custom module).

The primary tool for this is hook_theme_suggestions_HOOK_alter(). This hook allows you to inspect the variables being passed to a template and append new naming conventions to the $suggestions array.

Handling Standard Core Blocks

For blocks placed via the standard Block UI (/admin/structure/block), the region information is stored within the Block entity. Here is how you can retrieve that data and add it to your suggestions:

use Drupal\block\Entity\Block;

/**
 * Implements hook_theme_suggestions_block_alter().
 */
function MODULE_theme_suggestions_block_alter(array &$suggestions, array $variables) {
  // Check if the block has an ID, which indicates it is a placed block entity.
  if (!empty($variables['elements']['#id'])) {
    $block = Block::load($variables['elements']['#id']);

    if ($block) {
      $region = $block->getRegion();
      $block_id = $variables['elements']['#id'];

      // Add a suggestion for the region: block--regionname.html.twig
      $suggestions[] = 'block__' . $region;

      // Add a suggestion for the region and the specific block ID: block--regionname--id.html.twig
      $suggestions[] = 'block__' . $region . '__' . $block_id;
    }
  }
}

Support for Page Manager and Panels

If your site uses the Page Manager or Panels modules, blocks are often handled as configuration rather than standard Block entities. In these cases, the #id might be missing, but the region information is usually tucked away in the #configuration key.

You can extend your hook to handle both scenarios seamlessly:

function MODULE_theme_suggestions_block_alter(array &$suggestions, array $variables) {
  // Standard Block Entity approach
  if (!empty($variables['elements']['#id'])) {
    if ($block = \Drupal\block\Entity\Block::load($variables['elements']['#id'])) {
      $suggestions[] = 'block__' . $block->getRegion() . '__' . $variables['elements']['#id'];
    }
  }
  // Page Manager / Panels approach
  else if (isset($variables['elements']['#configuration']['region'])) {
    $region = $variables['elements']['#configuration']['region'];
    $plugin_id = $variables['elements']['#plugin_id'];

    // Sanitize the plugin ID to create a valid template name
    $parts = explode(':', $plugin_id);
    $id = end($parts);

    $suggestions[] = 'block__page_' . $region . '__' . $id;
  }
}

Naming Your Twig Templates

Once you have implemented the code above and cleared your Drupal cache, your theme will recognize new naming patterns. When Drupal looks for a template, it moves from the most specific suggestion to the least specific.

Based on the code above, if you have a block with the ID search_form in the header region, you can now use any of these filenames:

  1. block--header--search-form.html.twig (Most specific: Target this exact block in this exact region)
  2. block--header.html.twig (Target all blocks inside the header region)
  3. block--search-form.html.twig (Standard Drupal suggestion: Target this block anywhere)
  4. block.html.twig (The base fallback)

Note: Remember to replace underscores (_) with hyphens (-) in your actual .html.twig filenames, as is standard in Drupal's template naming convention.

Common Mistakes to Avoid

  • Forgetting to Clear Cache: Drupal caches theme registry information. If you add the hook but don't see your changes, run drush cr or clear the cache via the UI.
  • Missing Namespace: Ensure you include use Drupal\block\Entity\Block; at the top of your file. Without this, calling Block::load() will result in a fatal PHP error.
  • Layout Builder Blocks: Layout Builder does not utilize the standard Block entity regions in the same way. If you are using Layout Builder, you may need to inspect the #configuration array specifically for layout sections rather than the entity region.
  • Over-Engineering: Only add these suggestions if you truly need different HTML. If you only need different styling, consider adding a CSS class to the region wrapper in region.html.twig instead.

Frequently Asked Questions

Why was this feature removed from Drupal core?

In Drupal 7, blocks were strictly tied to regions. In modern Drupal, the same block instance can be reused in different contexts. To keep the core system clean and performant, Drupal only provides suggestions based on the block's identity, leaving contextual suggestions like "region" to be added by developers as needed.

Does this work for custom block types (Block Content)?

Yes. While the code above targets the block instance (the placement), it will work regardless of whether the block is a system block (like the search form) or a custom block type (like a 'Basic' block). If you need to target by block type specifically, you would use $variables['elements']['content']['#block_content']->bundle().

Will this affect site performance?

Adding a few string suggestions to an array has a negligible impact on performance. However, always ensure your logic inside hook_theme_suggestions_alter is lightweight. Avoid heavy database queries or complex API calls within this hook, as it runs every time a block is prepared for rendering.

Wrapping Up

Restoring region-based theme suggestions is a common requirement for complex Drupal themes. By using hook_theme_suggestions_block_alter, you gain the granular control needed to style your site effectively while maintaining the flexibility of the modern Drupal block system.

Whether you are working with standard core regions or Page Manager layouts, the ability to target block--regionname.html.twig ensures your markup remains clean, semantic, and exactly where you need it to be. Happy theming!