When Block Visibility Belongs on the Content, Not the Placement

Block Content Visibility

Drupal core attaches visibility to where a block is placed. On a growing class of site, that is the wrong entity, and you can tell which class you are in by four questions.

We just released Block Content Visibility, with BloomIdea, my agency, as the supporting organization on it. The case rests on Drupal core's own model and a public core issue.

The claim is this: Drupal core gives you two layers of block visibility and both are attached to the wrong thing for an increasingly common kind of site. For reusable content blocks on multilingual, moderated, or permission-split sites, visibility belongs on the block_content entity, not on the placement, and core currently gives you nowhere to put it there. That is contestable, a senior site builder will contest it, and I will give their strongest version before answering it.

The two layers core ships, and where they sit

The first layer is the condition plugin system: User Role, Request Path, Language, and whatever else core and contrib expose. The second layer is placement. When you place a block at /admin/structure/block/add/<id>, BlockForm renders those condition plugins and stores your answers on the placement, which is a block config entity. This is the right model, and a good one, when a block is placed once in one region and the rule that decides whether it appears is a property of that spot on the page.

It stops being the right model the moment the rule is a property of the content rather than the spot.

Take a single reusable block: a "VIP free shipping" promo. You place it in three regions across two themes. The rule "show this only to the VIP audience segment" is not a fact about the header, the sidebar, or the footer. It is a fact about the promo. Core makes you re-enter it on all three placements and keep them synchronized by hand forever. Change the segment and you change it in three places, or you miss one and the rule silently desynchronizes. Nothing tells you it drifted.

Now add three things that are normal on enterprise and public-sector Drupal, not exotic:

  • The block is translated. Placement config and content translation live in different storage. The rule that should travel with the translated content does not.
  • The block is under content moderation. You want visibility versioned with the content, so reverting a revision reverts its visibility too. Placement config is not revisioned with the entity.
  • The people who manage the block content do not have administer blocks. This is a deliberate, common split: editors own content, site builders own structure. Core's only home for visibility is the structure side, so either editors cannot set it or you over-grant a structural permission to content people to work around the model.

These are the same defect from three angles. The rule is a property of the content, and the only place core lets you store it is the placement.

What Block Content Visibility does, concretely

It adds the missing third layer: content-level visibility. It attaches a single visibility_conditions base field to block_content, a revisionable, single-value string_long field holding a JSON payload, and renders core's existing Condition Plugin UI directly on the standard block content add/edit form. The same UI appears inside the Layout Builder inline-block modal, so an editor composing a page can gate a block on a role or cookie condition without leaving Layout Builder. There is a dedicated administer block content visibility permission, so the capability follows the content-versus-structure boundary instead of breaking it, and a settings form at /admin/config/system/block-content-visibility to opt bundles in and hide noisy plugins. You install it with composer require drupal/block_content_visibility.

Two design choices carry the entire argument, and they are choices, not details.

It contributes zero new condition plugins. It is plumbing. Its value is exactly the plugins you already have enabled, core, contrib, or your own, surfaced on the entity instead of only on the placement. It adds a place to put a rule, not new rules.

It AND-combines with placement-level visibility rather than replacing it. At render time it resolves the underlying entity, builds a ConditionPluginCollection, applies per-condition runtime contexts, and returns an access result, mirroring how core's own BlockAccessControlHandler already works, with full cache-metadata bubbling. Placement visibility still works and still means what it meant. Content visibility is an additional gate that travels with the content. A block shows only if both layers allow it. That coexistence is the answer to the main objection.

The strongest objection: placement-level is correct by design

A senior site builder will push back immediately, and the strong form is right on its own terms. Binding visibility to the entity is wrong because the same reusable block legitimately should be able to behave differently in different placements. The footer instance and the sidebar instance are not the same situation, and the placement is exactly where that difference belongs.

I agree with that completely, and it is why the module AND-combines instead of overriding. Per-placement difference stays on the placement, correctly modeled. The claim is narrower and harder to refute: there exists a class of rule that is invariant across every placement of a block because it is a fact about the content, and core gives you no entity-level place to express it, so you are forced to duplicate it across placements or push the content's own logic into structure config. The module adds the missing layer for the invariant rule and leaves the per-placement layer untouched for the variant one. Both are needed. Core ships only one.

The other objection: core issue #2916876 will handle this

The canonical answer to "I need block visibility that Layout Builder respects" is core issue #2916876, "Add visibility control conditions to blocks within Layout Builder." State its status with the same precision I am asking for my own side. It was opened on 17 October 2017. It is priority Major. It is still "Needs work." It was last touched on 24 January 2026, with an active merge request against 11.x, so it is alive, not abandoned. Its own problem statement is that Layout Builder introduced a new placement paradigm without a visibility mechanism, which is a real gap and a serious effort to close it.

It is not a substitute for this layer, and the reason is architectural, not who ships first. By design, #2916876 stores conditions on the Layout Builder section or component. It therefore covers Layout Builder placements only, and the rule lives on the layout, not on the block_content entity. It does nothing for classic block placements, and it does not make visibility travel with the content through translation or moderation, because the layout is not the content. If it lands, it will be the correct fix for the variant, per-component case inside Layout Builder. It will still not be a place to put a rule that is a property of the content and must hold across every placement, classic and Layout Builder alike. Different layer, different scope, and after eight years, not a thing you can build against today.

Adjacent contrib has the same shape of mismatch. Block Visibility Groups groups blocks under shared site-admin conditions, a site-administration tool with a different permission audience that does not make a rule travel with one piece of content. Block Visibility Conditions contributes extra condition plugins but does not move where conditions are stored. None of them put the rule on the entity.

The decision rule

This is the useful part, and you can apply it without installing anything. For any block visibility rule you are about to configure, ask:

  • Is this rule the same for every placement of this block, or does it differ by spot on the page? Same everywhere points to content. Differs by spot points to placement.
  • Does the block get translated, and should the rule follow the translation? Yes points to content.
  • Is the block under content moderation, and should reverting a revision revert its visibility? Yes points to content.
  • Do the people who set this rule have administer blocks, or only content permissions? Content-only points to content.

If you answered "placement" to the first and the rest do not apply, core already serves you and you do not need this module. If you answered "content" to the first or yes to any of the others, core has no native home for the rule and you are choosing today between manual duplication, a structural permission over-grant, or rules that desynchronize. That gap is where Block Content Visibility belongs, specifically on multilingual, moderated, governance-heavy Drupal, which is also, in fairness, the kind of site BloomIdea is paid to build, and not as a default everywhere. It is a 1.0.0-beta1 release with a hard dependency on Block Form Alter and is not yet covered by Drupal's security advisory policy, which is worth knowing before you put it on production rather than after.

Core's placement model is not wrong. It is incomplete in a direction that matters more every year, as Drupal sites get more multilingual, more moderated, and stricter about the editor-versus-site-builder boundary. The missing piece is small, it is the right layer, and the layer is the entity, because the entity is where reuse, translation, moderation, and editorial ownership already live. Putting the rule anywhere else is putting it next to the data instead of on it.


José Fernandes is the founder of BloomIdea, a Drupal and AI agency based in Braga, Portugal. He is the author of Digital Marketing with Drupal (Packt, 2022), with a foreword by Drupal creator Dries Buytaert, and is completing a PhD on human-AI collaboration in organizational contexts at the University of Minho.