Wiki 9Minecraft Wiki 9Minecraft

MBE Pattern: Simulated Block Entities

Pair a custom block with an invisible anchor entity for inventories, animations, and dynamic data.

5Sections
4Code examples
4Known errors

Max's Block Entity (MBE) is a community-coined design pattern that pairs a custom block with a stationary, invisible entity placed inside the same block space. Because data-driven Bedrock blocks cannot store custom inventories, run skeletal animations, or hold arbitrary dynamic data on their own, creators use this hybrid approach to mimic true block entities like chests or furnaces. You reach for the MBE pattern when block states and block permutations are no longer enough to store the state your machine or furniture requires.

Why Bedrock Needs Simulated Block Entities

In the vanilla game, blocks like chests, brewing stands, and lecterns are backed by internal C++ block actors (often called tile entities). While the Bedrock add-on format allows you to define custom blocks with custom geometry and up to 64 block permutations, it does not expose a way to attach a custom container UI, dynamic scoreboard values, or complex bone animations directly to a block.

By spawning an entity at the exact center of a custom block, you gain access to the full entity component suite alongside the block's crisp voxel collision and placement rules. The block handles grid alignment, mining speed, and Piston protection, while the entity holds the `minecraft:inventory` component, stores dynamic properties or entity properties, and runs complex client-side animations. Historically, this glue logic was written using legacy holiday creator features or tick functions, which caused noticeable lag when players built large bases. In modern Bedrock (1.20.60 and later), the pairing is driven by the `@minecraft/server` Script API using block custom components, making it event-driven and far lighter on world performance.

FeatureHandled by BlockHandled by Entity
Grid placement & raycast outlineYes (`minecraft:selection_box`)No (hitbox is set to zero)
Mining time & tool requirementsYes (`minecraft:destructible_by_mining`)No (entity is invulnerable to hits)
Container slots & UINoYes (`minecraft:inventory`)
Skeletal & Molang animationsNo (static geometry only)Yes (full RP entity animation support)
Piston & explosion safetyYes (immovable / high resistance)Reacts if block is destroyed

The Host Block Definition

The host block serves as the physical shell of the MBE. Its primary jobs are to occupy the voxel space, prevent pistons from pushing the block out of alignment with its entity, and notify the Script API or command system when it is placed, interacted with, or broken.

When configuring the block, two mechanical edge cases must be locked down in JSON. First, if a piston pushes the block, the entity will be left behind in the old block space. Although the modern `minecraft:movable` component exists in recent versions to control piston behavior, if you are targeting wider compatibility or cannot recall the exact movement type syntax, making the block immovable via bedrock-style explosion resistance or simply cleaning up the entity when the block disappears is critical. Second, if you want the entity to render the visuals (for example, an animated spinning gear or a chest lid), the block itself should use an invisible material or a tiny empty geometry while keeping a full 16x16x16 `minecraft:collision_box` and `minecraft:selection_box`.

In the example below, the block uses a standard geometry and registers a custom component named `wiki:mbe_host` under `minecraft:custom_components`. This delegates placement and destruction lifecycle events to your behavior pack scripts without relying on depreciated JSON event responses.

BP/blocks/drawer_block.json
{
  "format_version": "1.20.60",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:drawer_block",
      "menu_category": {
        "category": "items"
      }
    },
    "components": {
      "minecraft:geometry": "geometry.wiki.drawer_block",
      "minecraft:material_instances": {
        "*": {
          "texture": "wiki_drawer_block",
          "render_method": "alpha_test"
        }
      },
      "minecraft:collision_box": {
        "origin": [-8, 0, -8],
        "size": [16, 16, 16]
      },
      "minecraft:selection_box": {
        "origin": [-8, 0, -8],
        "size": [16, 16, 16]
      },
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 2.5
      },
      "minecraft:destructible_by_explosion": {
        "explosion_resistance": 3600000
      },
      "minecraft:custom_components": [
        "wiki:mbe_host"
      ]
    }
  }
}

The Anchor Entity Definition

The anchor entity is a non-moving utility actor that sits inside the host block. A poorly configured anchor entity will suffocate inside the solid block, get pushed out by walking players, or block the player's cursor so they can never mine the underlying block.

To prevent the entity from intercepting the player's left-click or right-click raycast, set both the `width` and `height` inside `minecraft:collision_box` to `0.0`. Because its hitbox is zero, players cannot click the entity directly to open its inventory; we will bridge that interaction through the block in the next section. You also need `minecraft:physics` with both `has_gravity` and `has_collision` set to `false` so the entity floats cleanly at the center of the voxel and does not get ejected by the block's solid collision box.

To keep the entity alive and quiet, include `minecraft:Pushable` with both boolean flags false, `minecraft:persistent` so it never despawns when the player walks away, and a `minecraft:damage_sensor` that filters out all incoming damage. If the entity holds items for the block, attach `minecraft:inventory` here. When the entity is eventually killed by our cleanup script or self-check, it will naturally drop the contents of its inventory at the block's location.

BP/entities/drawer_entity.json
{
  "format_version": "1.20.60",
  "minecraft:entity": {
    "description": {
      "identifier": "wiki:drawer_entity",
      "is_spawnable": false,
      "is_summonable": true,
      "is_experimental": false
    },
    "components": {
      "minecraft:collision_box": {
        "width": 0.0,
        "height": 0.0
      },
      "minecraft:physics": {
        "has_gravity": false,
        "has_collision": false
      },
      "minecraft:pushable": {
        "is_pushable": false,
        "is_pushable_by_piston": false
      },
      "minecraft:persistent": {},
      "minecraft:fire_immune": true,
      "minecraft:knockback_resistance": {
        "value": 1.0
      },
      "minecraft:damage_sensor": {
        "triggers": [
          {
            "cause": "all",
            "deals_damage": false
          }
        ]
      },
      "minecraft:inventory": {
        "container_type": "container",
        "inventory_size": 27,
        "restrict_to_owner": false
      }
    }
  }
}

Binding Lifecycle with Block Custom Components

With the block and entity defined, you must link their lifecycles so that placing the block spawns the entity, and breaking the block removes it. In modern Bedrock add-ons, this is done by registering the `wiki:mbe_host` custom component during the `worldInitialize` event in your behavior pack script.

When `onPlace` fires, calculate the bottom-center of the block space by adding `0.5` to the block's `x` and `z` coordinates. Spawning the entity at integer coordinates (`block.location`) puts it on the northwest corner of the block, which causes visual misalignment and breaks distance-based entity queries. When `onPlayerDestroy` fires, use `dimension.getEntities` with a small `maxDistance` around the centered coordinate to locate the anchor entity. Before removing the entity, note that calling `.remove()` in Script API deletes the entity silently without dropping its inventory. Calling `.kill()` triggers normal death behavior, which drops the contents of `minecraft:inventory` into the world—provided your `minecraft:damage_sensor` allows `void` or `override` damage, or if you spill the items manually via script before calling `.remove()`.

The script below safely spawns the entity at the block center and manually transfers any stored items from the entity's `minecraft:inventory` component into the world as item entities before calling `.remove()`. This guarantees that items are never lost even when the entity is completely immune to damage.

BP/scripts/mbe_manager.js
import { world, system } from "@minecraft/server";

function getBlockCenter(location) {
  return {
    x: location.x + 0.5,
    y: location.y,
    z: location.z + 0.5
  };
}

function cleanupMbeEntity(dimension, location) {
  const center = getBlockCenter(location);
  const entities = dimension.getEntities({
    type: "wiki:drawer_entity",
    location: center,
    maxDistance: 0.4
  });

  for (const entity of entities) {
    const invComp = entity.getComponent("minecraft:inventory");
    if (invComp && invComp.container) {
      const container = invComp.container;
      for (let i = 0; i < container.size; i++) {
        const item = container.getItem(i);
        if (item) {
          dimension.spawnItem(item, center);
        }
      }
      container.clearAll();
    }
    entity.remove();
  }
}

world.beforeEvents.worldInitialize.subscribe((eventData) => {
  eventData.blockComponentRegistry.registerCustomComponent("wiki:mbe_host", {
    onPlace(event) {
      const { block, dimension } = event;
      const center = getBlockCenter(block.location);
      dimension.spawnEntity("wiki:drawer_entity", center);
    },
    onPlayerDestroy(event) {
      const { block, dimension } = event;
      cleanupMbeEntity(dimension, block.location);
    }
  });
});

Command Fallback and Self-Cleanup for Edge Cases

Even with `onPlayerDestroy` configured, blocks in Minecraft can be removed without a player mining them. Commands like `/fill` or `/setblock`, structure block loads, or creative mode tools can overwrite your custom block without firing player destruction events. If that happens, the invisible `wiki:drawer_entity` becomes an orphan floating in the air.

Also, remember that our anchor entity has a `0.0` collision box so the player can mine the block. If your entity needs to open its `minecraft:inventory` screen when the player clicks the block, there is a catch: the `@minecraft/server` API does not currently have a direct method to force a player to open another entity's native container UI from a block interaction event. If your primary goal is a native container GUI (like a 27-slot chest), you must compromise on the hitbox: give the entity a `0.8` or `0.9` collision box so the player's right-click hits the entity directly, and leave the outer edges of the block exposed for mining.

To solve the orphaned entity problem across all edge cases, add a slow `minecraft:timer` component to the entity in JSON, or run a low-frequency command check. Every couple of seconds, the entity executes a command that checks if the block at its feet (`~ ~ ~`) is still `wiki:drawer_block`. If the block was replaced by air or another block, the entity runs a cleanup function or triggers a despawn component group using `minecraft:instant_despawn`.

BP/entities/drawer_entity_watchdog.json
{
  "format_version": "1.20.60",
  "minecraft:entity": {
    "description": {
      "identifier": "wiki:drawer_entity",
      "is_spawnable": false,
      "is_summonable": true
    },
    "component_groups": {
      "wiki:despawn_now": {
        "minecraft:instant_despawn": {}
      }
    },
    "components": {
      "minecraft:collision_box": {
        "width": 0.0,
        "height": 0.0
      },
      "minecraft:physics": {
        "has_gravity": false,
        "has_collision": false
      },
      "minecraft:timer": {
        "looping": true,
        "time": 2.0,
        "time_down_event": {
          "event": "wiki:verify_block",
          "target": "self"
        }
      }
    },
    "events": {
      "wiki:verify_block": {
        "filters": {
          "test": "is_block",
          "subject": "self",
          "operator": "not",
          "value": "wiki:drawer_block"
        },
        "add": {
          "component_groups": [
            "wiki:despawn_now"
          ]
        }
      }
    }
  }
}

Which versions this applies to

Applies to Bedrock Edition 1.20.60 and newer. Block custom components (`minecraft:custom_components`) and the stable `@minecraft/server` API reached general availability in 1.21.50; in versions between 1.20.60 and 1.21.40, you must enable the Beta APIs experimental toggle if using experimental script methods, though basic custom component registration became stable in 1.21.

When it does not work

What you seeWhyThe fix
The player cannot break the block, or right-clicking opens the inventory but left-clicking attacks the air without mining.The anchor entity's `minecraft:collision_box` is equal to or larger than `1.0` (16 pixels), completely enveloping the block's `minecraft:selection_box` and intercepting all player raycasts.Set the entity's collision box `width` and `height` to `0.0` if using scripts for interaction, or shrink it to `0.75` and adjust the block's selection box so the player can click the entity center for UI and the block corners to mine.
Entity spawns on the corner of the block and looks off-center, or the cleanup script fails to find the entity when the block is broken.Block coordinates in Bedrock are whole integers representing the minimum corner (`x, y, z`) of the voxel, whereas entities spawn centered on their exact coordinate.Always add `0.5` to the `x` and `z` coordinates of `block.location` both when calling `spawnEntity` and when searching with `getEntities`.
Content Log shows: `[Scripting][error]-TypeError: Cannot read properties of undefined (reading 'registerCustomComponent')` or the custom component does nothing.You attempted to register the block custom component outside of the `worldInitialize` before-event, or your `manifest.json` is targeting an older `@minecraft/server` version that predates the registry.Wrap `blockComponentRegistry.registerCustomComponent` inside `world.beforeEvents.worldInitialize.subscribe(...)` and update your `@minecraft/server` dependency in `BP/manifest.json` to `1.12.0` or newer.
Items stored inside the MBE vanish completely when the player mines the block.Calling `entity.remove()` in the Script API or triggering `minecraft:instant_despawn` erases the entity from memory without firing death loot tables or spilling `minecraft:inventory` contents.Iterate over the entity's `minecraft:inventory` container slots in your `onPlayerDestroy` script and call `dimension.spawnItem()` for each slot before calling `entity.remove()`.

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.