With the release of Drupal, an important rendering behavior has changed for block content render arrays. If your custom modules or themes rely on #attributes in block render arrays, this update may affect your frontend output.
The change was introduced in the Drupal core issue:
“Attributes of a block content are applied to block itself” (#2486267)
This article explains what changed, why it matters, and how to update your code correctly.
What Changed?
Before Drupal 11.4, attributes defined in a block render array under #attributes were automatically merged into the outer wrapper of the entire block.
Starting from Drupal 11.4, those attributes are now applied to the inner content wrapper instead.
To target the outer block container, developers must now use #wrapper_attributes.
Previous Behavior (Before Drupal 11.4)
Example render array:
return [
'#attributes' => [
'class' => [
'foo',
],
'data-content-custom' => 'bar',
],
'#wrapper_attributes' => [
'data-wrapper-custom' => 'baz',
],
'#markup' => 'Sample content',
];
Generated HTML:
<div id="block-foobartest"
class="foo"
data-content-custom="bar"
data-wrapper-custom="baz">
<div class="content">
Sample content.
</div>
</div>
Here, everything from #attributes was merged into the outer block wrapper.
New Behavior in Drupal 11.4+
The same render array now produces:
<div id="block-foobartest"
data-wrapper-custom="baz">
<div class="content foo"
data-content-custom="bar">
Sample content.
</div>
</div>
Now:
#wrapper_attributes→ applied to the outer block wrapper#attributes→ applied to the.contentwrapper
Why This Change Was Made
This behavior is more semantically correct and predictable.
Previously, developers often expected:
#attributes→ content-level attributes#wrapper_attributes→ wrapper/container attributes
But Drupal internally merged both onto the outer block container, creating confusion and inconsistencies.
The new behavior clearly separates:
| Purpose | Property |
|---|---|
| Entire block container | #wrapper_attributes |
| Inner block content | #attributes |
This makes block rendering easier to reason about and aligns better with render API expectations.
Who Is Affected?
This change impacts:
- Custom module developers
- Theme developers
- Site builders using preprocess hooks
- Anyone injecting classes/data attributes into block render arrays
Especially affected:
- CSS selectors targeting block wrappers
- JavaScript relying on wrapper-level data attributes
- Layout Builder integrations
- Custom Twig templates expecting wrapper classes
Common Problems After Upgrading
After updating to Drupal 11.4, you may notice:
CSS styles stopped working
Example:
.foo {
margin-bottom: 20px;
}
Previously .foo was on the outer block wrapper.
Now it is attached to .content.
JavaScript selectors break
Example:
document.querySelector('[data-content-custom]');
The attribute may now exist deeper in the DOM than expected.
Layout/styling inconsistencies
Flexbox, grid, spacing, or container-level styling may shift because classes moved from parent wrappers to child content wrappers.
How to Fix Existing Code
If you intended attributes to apply to the outer block wrapper, move them to #wrapper_attributes.
Old code
return [
'#attributes' => [
'class' => ['hero-block'],
],
];
Updated code
return [
'#wrapper_attributes' => [
'class' => ['hero-block'],
],
];
Recommended Best Practice Going Forward
Use:
'#wrapper_attributes'
for:
- Layout classes
- Grid/flex container behavior
- Wrapper-level JavaScript hooks
- Spacing utilities
Use:
'#attributes'
for:
- Content styling
- Inner content behavior
- Accessibility attributes related to content
- Content-specific JS targeting
Migration Checklist
After upgrading to Drupal 11.4:
- Audit custom block plugins
- Review preprocess functions
- Check custom themes
- Test JavaScript selectors
- Inspect rendered HTML in browser DevTools
- Search for
#attributesusage in block render arrays
Useful command:
grep -R "#attributes" web/modules/custom
Final Thoughts
This is a relatively small change in Drupal core, but it can have a noticeable impact on frontend behavior in existing projects.
The good news is that the migration path is straightforward:
- Wrapper attributes →
#wrapper_attributes - Content attributes →
#attributes
Once updated, your render arrays will be more explicit, maintainable, and aligned with Drupal’s rendering architecture moving forward.
Comments