What we cover
Give me the TL;DR

If you've ever placed the same reusable block in multiple regions of a Layout Builder page and found that only the first instance renders while every subsequent placement shows nothing. You've hit a known Drupal core issue. It's frustrating, and the workaround isn't obvious.

What's Going On

Drupal's render cache uses a block's entity UUID as part of its cache key. When you create a reusable block (block_content entity) every placement on the same page shares the same entity UUID. Drupal's caching system sees them as identical and only renders the first instance — subsequent placements return the cached empty result from earlier in the render pipeline.

The same UUID collision also causes the Layout Builder admin UI to struggle. When you try to edit or remove the second or third instance of a reusable block, the UI can't reliably distinguish between placements, making those blocks effectively locked in place and uneditable.

This is a documented Drupal core issue that has been around for a while, and a patch hasn't landed in core yet. If your site relies heavily on reusable blocks in Layout Builder — which is a very reasonable architectural choice — you'll need a module-level solution.

The Fix: A Custom Module

The solution is to intercept Layout Builder's render pipeline and add unique cache keys based on each component's UUID (which is unique per placement) rather than the block entity's UUID (which is shared). Here is the custom module that I created to solve this issue.

Module Structure

web/modules/custom/lb_reusable_fix/
├── lb_reusable_fix.info.yml
├── lb_reusable_fix.services.yml
└── src/
   └── EventSubscriber/
       └── LayoutBuilderRenderSubscriber.php

lb_reusable_fix.info.yml

name: 'Layout Builder Reusable Block Fix'
type: module
description: 'Fixes render caching for reusable blocks placed multiple times in Layout Builder.'
core_version_requirement: ^10 || ^11
package: Custom
dependencies:
 - drupal:layout_builder
 - drupal:block_content

lb_reusable_fix.services.yml

services:
 lb_reusable_fix.render_subscriber:
   class: Drupal\lb_reusable_fix\EventSubscriber\LayoutBuilderRenderSubscriber
   tags:
     - { name: event_subscriber }

src/EventSubscriber/LayoutBuilderRenderSubscriber.php

<?php
namespace Drupal\lb_reusable_fix\EventSubscriber;

use Drupal\Core\Render\Element;
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
use Drupal\layout_builder\LayoutBuilderEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* Adds unique cache keys to reusable block placements in Layout Builder.
*
* Prevents render cache collisions when the same block_content entity is
* placed multiple times on a Layout Builder page.
*/
class LayoutBuilderRenderSubscriber implements EventSubscriberInterface {
 /**
  * {@inheritdoc}
  */
 public static function getSubscribedEvents(): array {
   return [
     LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY => [
       'onBuildRender', -100,
     ],
   ];
 }
 /**
  * Adds a unique cache key per component UUID to prevent cache collisions.
  */
 public function onBuildRender(SectionComponentBuildRenderArrayEvent $event): void {
   $build = $event->getBuild();
   $component = $event->getComponent();
   // Add the component UUID as a unique cache key.
   $component_uuid = $component->getUuid();
   if (!empty($component_uuid)) {
     if (!isset($build['#cache']['keys'])) {
       $build['#cache']['keys'] = [];
     }
     $build['#cache']['keys'][] = 'lb_component_' . $component_uuid;
     $event->setBuild($build);
   }
 }
}

Installation

  1. Place the module in web/modules/custom/lb_reusable_fix/
  2. Enable it: drush en lb_reusable_fix
  3. Rebuild cache: drush cr

No configuration needed. Existing reusable block placements will start rendering correctly on the next page load or after a cache clear.

What This Changes (and What It Doesn't)

What changes: Each Layout Builder component gets its own cache entry. Duplicate placements of the same reusable block entity will all render independently, and the admin UI should correctly target each placement for editing or removal.

What doesn't change: Your content editing workflow stays exactly the same. You're still working with reusable blocks, and edits to the block entity still propagate everywhere it's placed because the block content itself is still the same entity. Only the render cache entry is now unique per placement.

Known limitation: This does increase cache storage slightly, since placements that were previously sharing a cache entry now each have their own. On sites with very large numbers of Layout Builder pages and heavily reused blocks, monitor cache size if you're using a memory-based cache backend.

This is one of those issues that looks like a configuration problem but is actually a core cache architecture quirk. Once you understand why it happens, the fix is straightforward — and it keeps your existing reusable block workflow intact.

Share this post