Wiki 9Minecraft Wiki 9Minecraft

Block Co-Location in Custom Blocks

How to place multiple blocks or fluids in the same grid space using Bedrock block states and permutations.

5Sections
3Code examples
3Known errors

Block co-location is the practice of making a single world grid space appear to hold more than one block or fluid at the same time. Because the Bedrock engine does not expose a native multi-block container layer for data-driven custom blocks, creators achieve co-location through three distinct approaches: waterlogging via liquid detection, state-driven geometry clusters, and the engine's internal two-layer block storage. Choosing the right technique depends on whether you are mixing a block with water, stacking custom models like candles and sea pickles, or manipulating snow-covered blocks via scripts.

How Bedrock Handles Shared Grid Spaces

At the engine level, a Bedrock chunk actually contains two block storage layers per coordinate. Layer 0 holds the primary block, while Layer 1 is reserved almost exclusively for water (producing waterlogged blocks) and occasionally flowing lava or snow in specific vanilla mechanics. You cannot arbitrarily place two unrelated custom blocks into Layer 0 and Layer 1 through standard JSON definitions.

When you want a custom block to share its space with something else, you either configure the block to allow Layer 1 water using the liquid detection component, or you fake the co-location entirely within Layer 0. Faking it means combining the visuals and hitboxes of both objects into one block definition and toggling them with block states.

Co-Locating with Water (Waterlogging)

The only true multi-layer co-location exposed to custom block JSON is waterlogging. By adding the `minecraft:liquid_detection` component to your block's behavior file, you instruct the game on how Layer 0 should react when water enters Layer 1 of the same block space.

Inside `detection_rules`, setting `can_contain_liquid` to true allows a player to place a water bucket directly into the custom block's space, or allows surrounding water to flow into it depending on the `liquid_type` and `on_liquid_touches` settings. For this to look right, your block must use a non-full `minecraft:geometry` and configure `minecraft:material_instances` with a render method such as `alpha_test` or `blend` so the water in the shared space remains visible.

FieldWhat it doesNotes
liquid_typeSpecifies which fluid the rule applies to.Currently only "water" is supported by the schema.
can_contain_liquidAllows water to occupy the secondary layer of this block space.Requires non-full geometry to be visually noticeable.
on_liquid_touchesDefines how the block reacts when flowing water reaches it.Accepts "blocking", "broken", "popped", or "no_reaction".
BP/blocks/stone_pillar.json
{
  "format_version": "1.21.50",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:stone_pillar"
    },
    "components": {
      "minecraft:geometry": "geometry.stone_pillar",
      "minecraft:material_instances": {
        "*": {
          "texture": "stone_pillar",
          "render_method": "alpha_test"
        }
      },
      "minecraft:collision_box": {
        "origin": [-4, 0, -4],
        "size": [8, 16, 8]
      },
      "minecraft:selection_box": {
        "origin": [-4, 0, -4],
        "size": [8, 16, 8]
      },
      "minecraft:liquid_detection": {
        "detection_rules": [
          {
            "liquid_type": "water",
            "can_contain_liquid": true,
            "on_liquid_touches": "no_reaction"
          }
        ]
      }
    }
  }
}

Same-Block Clustering (The Sea Pickle Pattern)

When vanilla Minecraft lets you place up to four sea pickles, candles, or turtle eggs into a single block space, it is not actually co-locating four separate block instances. Instead, it uses a single block with an integer block state that tracks how many items have been added, swapping out the geometry and expanding the collision box at each step.

To replicate this in your own add-on, define an integer state in your block description and create one bone per cluster piece in your Blockbench model. You can either export separate geometry files for each count, or use a single geometry file with bone visibility controlled inside the `minecraft:geometry` component. Pair this with a custom component in the Script API to listen for player interactions and increment the state.

BP/blocks/pebble_cluster.json
{
  "format_version": "1.21.50",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:pebble_cluster",
      "states": {
        "wiki:count": [1, 2, 3, 4]
      }
    },
    "components": {
      "minecraft:geometry": {
        "identifier": "geometry.pebble_cluster",
        "bone_visibility": {
          "pebble_1": true,
          "pebble_2": "q.block_state('wiki:count') >= 2",
          "pebble_3": "q.block_state('wiki:count') >= 3",
          "pebble_4": "q.block_state('wiki:count') >= 4"
        }
      },
      "minecraft:material_instances": {
        "*": {
          "texture": "pebble",
          "render_method": "alpha_test"
        }
      },
      "minecraft:custom_components": [
        "wiki:cluster_interact"
      ]
    },
    "permutations": [
      {
        "condition": "q.block_state('wiki:count') == 1",
        "components": {
          "minecraft:selection_box": {
            "origin": [-3, 0, -3],
            "size": [6, 2, 6]
          },
          "minecraft:collision_box": false
        }
      },
      {
        "condition": "q.block_state('wiki:count') >= 2",
        "components": {
          "minecraft:selection_box": {
            "origin": [-6, 0, -6],
            "size": [12, 3, 12]
          },
          "minecraft:collision_box": false
        }
      }
    ]
  }
}

Handling Cluster Placement in Script API

Because a custom block will normally cause the player to place a brand-new block against the side of the existing one, you must intercept the interaction in script using the `onPlayerInteract` hook of a custom component. When the player right-clicks or taps the block while holding the matching item, you increment the `wiki:count` permutation state and decrement their held item stack.

Remember to also update your block's loot table (`minecraft:loot`) or script a `onPlayerDestroy` event so that breaking a cluster with `wiki:count` set to 3 actually drops three items instead of one.

BP/scripts/cluster.js
import { world, EquipmentSlot, GameMode } from "@minecraft/server";

world.beforeEvents.worldInitialize.subscribe((event) => {
  event.blockComponentRegistry.registerCustomComponent("wiki:cluster_interact", {
    onPlayerInteract(e) {
      const { block, player, dimension } = e;
      if (!player) return;

      const equippable = player.getComponent("minecraft:equippable");
      const mainhand = equippable?.getEquipmentSlot(EquipmentSlot.Mainhand);
      if (!mainhand?.hasItem() || mainhand.typeId !== "wiki:pebble_cluster") return;

      const currentCount = block.permutation.getState("wiki:count");
      if (typeof currentCount !== "number" || currentCount >= 4) return;

      block.setPermutation(block.permutation.withState("wiki:count", currentCount + 1));
      dimension.playSound("dig.stone", block.location);

      if (player.getGameMode() !== GameMode.creative) {
        if (mainhand.amount > 1) {
          mainhand.amount -= 1;
        } else {
          mainhand.setItem(undefined);
        }
      }
    }
  });
});

Co-Locating Two Distinct Custom Blocks

Sometimes you want two different items—such as a custom fence post and a custom vine, or a pipe and a casing—to occupy the exact same space. Since Layer 1 is strictly locked to fluids in custom blocks, you have to build a combined block definition or add a boolean state to the base block.

If the combination set is small, add a boolean state like `wiki:has_vine` directly onto the fence block, and include the vine geometry as a hidden bone that turns visible when `q.block_state('wiki:has_vine')` is true. If you have many combinations, keep the base blocks clean and swap the block entirely using `block.setType("wiki:fence_with_vine")` when the player interacts with the second item. In both cases, you must handle the extra item drop manually using a loot table condition or script when the combined block is broken.

Which versions this applies to

Applies to Bedrock Edition 1.20.60 and newer for `minecraft:liquid_detection` and `bone_visibility` in stable JSON schemas. The `minecraft:custom_components` scripting workflow requires format_version 1.21.10 or higher (stable, no experimental toggles needed in current releases).

When it does not work

What you seeWhyThe fix
[Blocks][error]-wiki:stone_pillar | minecraft:liquid_detection | failed to parseThe block JSON `format_version` is older than 1.20.60, or `detection_rules` was written as a single JSON object instead of an array of objects.Update `format_version` to at least "1.20.60" (or "1.21.50") and wrap your rule object inside square brackets `"detection_rules": [ { ... } ]`.
Water placed inside the custom block turns the empty space around the model completely opaque blue or black.The block's `minecraft:material_instances` component is defaulting to the `opaque` render method, or it is using the legacy full-block unit cube geometry.Specify a custom `minecraft:geometry` and set `"render_method": "alpha_test"` or `"blend"` inside `minecraft:material_instances`.
Clicking the cluster block places a new block on the adjacent tile instead of increasing the cluster count.The player is holding the block item, and the client processes normal block placement before or alongside the interaction unless the script consumes the interaction on the existing block.Make sure `minecraft:custom_components` is present on the active permutation, verify your script registers the component inside `worldInitialize`, and consider using a separate item identifier (`minecraft:item`) for placing and stacking the cluster so you have full control over `onUseOn`.

Reference, straight from the game files

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