Wiki 9Minecraft Wiki 9Minecraft

Custom Block States and Permutations

Define custom properties on blocks and swap components dynamically using permutations.

5Sections
4Code examples
4Known errors

Block states are named variables attached to a custom block that let a single block identifier behave and look differently depending on its current value. Instead of registering separate blocks for an open crate and a closed crate, or four separate blocks for each compass direction, you define a state in the block description and swap components in and out using permutations. Every combination of state values is pre-calculated when the world loads.

Declaring states in the description

Custom states live inside the description object of your behavior pack block file, under the states key. (In older format versions prior to 1.19.70, this key was called properties, which is now deprecated.) Every custom state name must use a namespace prefix, just like your block identifier, to prevent collisions with vanilla states. The minecraft: namespace is reserved and will throw a content error if you try to declare your own state under it.

When you list the allowed values for a state, the order of the array matters. The first value in the array becomes the default value whenever the block is placed in the world without an explicit state override. Bedrock supports three value types for states: booleans, integers, and strings. You can also specify a contiguous range of integers using an object with min and max keys instead of writing out every number in the array.

State TypeExample DefinitionNotes
Boolean[false, true]First value is the default on placement.
Integer array[0, 1, 2, 3]Values must be whole numbers.
Integer range{"values": {"min": 0, "max": 15}}Shorthand for contiguous integer sequences.
String["north", "south", "east", "west"]Strings are matched using single quotes in Molang.
BP/blocks/copper_lamp.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:copper_lamp",
      "states": {
        "wiki:lit": [false, true],
        "wiki:power_level": {
          "values": { "min": 0, "max": 3 }
        },
        "wiki:mode": ["normal", "flicker", "pulse"]
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 2.0
      }
    }
  }
}

Permutation limits and memory budget

Declaring a state does not change the block on its own; it only reserves the variable. Under the hood, Minecraft generates a separate block permutation for every possible combination of all states on your block. If a block has a boolean state (2 values), an integer state with 4 values, and a string state with 3 values, the engine generates 2 × 4 × 3 = 24 total permutations.

A single custom block is strictly capped at 65,536 total permutations. In practice, you should stay well below a few hundred permutations per block. High permutation counts increase world load times and consume significant memory, because the game builds component lookup tables for every combination upfront. If you find yourself storing large counters or arbitrary coordinates in block states, consider using a block entity or Script API dynamic properties instead.

Applying components with permutations

To make a block actually react to its states, you add a permutations array at the root of the minecraft:block object, alongside description and components. Each entry in the permutations array contains a condition string written in Molang and a components object.

When the block's current state causes the condition to evaluate to true, the components inside that permutation are merged over the base components of the block. If a component is defined in both the base components list and an active permutation, the permutation's version wins. If multiple permutations evaluate to true at the same time, their components are applied in the order they appear in the JSON array, meaning a later permutation will overwrite an earlier one if both touch the same component.

To read a state inside a condition string, use the q.block_state('namespace:state_name') Molang query. Boolean states can be checked directly or compared against true and false. String states must be compared against single-quoted string literals inside the JSON string.

BP/blocks/copper_lamp.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:copper_lamp",
      "states": {
        "wiki:lit": [false, true],
        "wiki:color": ["warm", "cool"]
      }
    },
    "components": {
      "minecraft:geometry": "geometry.copper_lamp",
      "minecraft:light_emission": 0,
      "minecraft:material_instances": {
        "*": {
          "texture": "copper_lamp_off",
          "render_method": "opaque"
        }
      }
    },
    "permutations": [
      {
        "condition": "q.block_state('wiki:lit') == true && q.block_state('wiki:color') == 'warm'",
        "components": {
          "minecraft:light_emission": 15,
          "minecraft:material_instances": {
            "*": {
              "texture": "copper_lamp_warm",
              "render_method": "opaque",
              "ambient_occlusion": false
            }
          }
        }
      },
      {
        "condition": "q.block_state('wiki:lit') == true && q.block_state('wiki:color') == 'cool'",
        "components": {
          "minecraft:light_emission": 12,
          "minecraft:material_instances": {
            "*": {
              "texture": "copper_lamp_cool",
              "render_method": "opaque",
              "ambient_occlusion": false
            }
          }
        }
      }
    ]
  }
}

Built-in placement traits

If you need standard directional states—such as facing the player who placed the block, or tracking which block face was clicked—you do not need to write custom states or script logic to calculate the angles. Bedrock provides traits inside the description object that automatically inject and set official minecraft: states when the block is placed.

The minecraft:placement_direction trait can enable minecraft:cardinal_direction (north, south, east, west) and minecraft:facing_direction (which adds up and down). The minecraft:placement_position trait can enable minecraft:block_face (the face of the neighbor block that was clicked) and minecraft:vertical_half (bottom or top, useful for slabs and stairs). Because these states are managed by a trait, you query them in your permutations using the minecraft: namespace.

BP/blocks/carved_pillar.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:carved_pillar",
      "traits": {
        "minecraft:placement_direction": {
          "enabled_states": ["minecraft:cardinal_direction"],
          "y_rotation_offset": 180.0
        }
      }
    },
    "components": {
      "minecraft:geometry": "geometry.carved_pillar"
    },
    "permutations": [
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'north'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 0, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'south'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 180, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'west'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 90, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'east'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 270, 0] }
        }
      }
    ]
  }
}

Reading and changing states in commands and Script API

Once your states are registered in JSON, you can set or test them in commands using square-bracket state syntax: /setblock ~ ~ ~ wiki:copper_lamp ["wiki:lit"=true, "wiki:color"="cool"]. Notice that inside command brackets, the state name is quoted, the separator is an equals sign, and string values use double quotes.

To change states dynamically during gameplay without replacing the block via commands, use the @minecraft/server Script API. Every Block instance has a permutation property representing its current BlockPermutation. Calling block.permutation.withState('namespace:state', newValue) returns a new permutation object with that single state updated while preserving all other states on the block. You then apply it back to the world with block.setPermutation().

BP/scripts/lamp_toggle.js
import { world } from "@minecraft/server";

world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
  const { block, isFirstEvent } = event;
  if (!isFirstEvent || block.typeId !== "wiki:copper_lamp") return;

  const currentLit = block.permutation.getState("wiki:lit");
  if (currentLit === undefined) return;

  const nextPermutation = block.permutation.withState("wiki:lit", !currentLit);

  event.cancel = true;
  block.setPermutation(nextPermutation);
});

Which versions this applies to

Stable in format_version 1.19.70 and above (traits require 1.20.20+). No experimental toggles are required for custom states, permutations, or placement traits.

When it does not work

What you seeWhyThe fix
Content Log Error: [Blocks][error]-wiki:copper_lamp | lit | Invalid state name, state names must be namespaced.You declared a custom state inside description.states without a colon and namespace prefix, or you tried to use the reserved 'minecraft:' prefix for a custom state.Rename the state key to include your pack's namespace, such as 'wiki:lit', and update all q.block_state() queries to match.
Permutations using string states never activate, or the Content Log reports a Molang syntax error.You omitted quotes around the string value inside the condition, or used escaped double quotes instead of single quotes. Without quotes, Molang treats the word as a variable or query name.Wrap string values in single quotes inside the condition expression: "q.block_state('wiki:color') == 'warm'".
Content Log Error stating the block exceeds the maximum number of permutations (65536).You declared too many states or large integer ranges on a single block. Because permutations multiply together across every state, five integer states ranging from 0 to 15 already produce 1,048,576 combinations.Reduce the number of distinct values per state, split distinct variants into separate block identifiers, or move data storage to the Script API.
A component changed by one permutation fails to revert when the state changes, or gets stuck on another permutation's value.Either the base components object does not define a fallback value for that component, or two permutations in your array both evaluate to true at the same time and both define the same component.Always define the default state of the component in the base components block, and make sure permutations that touch the same component have mutually exclusive Molang conditions.

Reference, straight from the game files

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