How format_version Controls Block Parsing
Every custom block JSON file in a behavior pack begins with a `format_version` string. This string does not necessarily have to match the exact version of Minecraft you are running, nor does it need to match the `min_engine_version` in your pack's `manifest.json`. Instead, it acts as a contract with the game's JSON parser, dictating which components are valid, how their fields are structured, and whether experimental world toggles are required to load the block.
When Mojang updates a block component—such as renaming a field or changing a boolean into an object—they gate that change behind a newer `format_version`. This ensures backward compatibility so older add-ons continue to function when players update their game, unless the underlying experimental feature itself is completely removed from the engine.
| Format Era | Key Characteristics | Current Status |
|---|---|---|
| 1.16.0 | Basic data-driven blocks; geometry and textures defined in RP `blocks.json`. | Legacy, still parses for simple blocks |
| 1.16.100 - 1.18.x | Holiday Creator Features era; introduced permutations, custom geometry, and JSON events. | Deprecated; JSON events removed in 1.20.80+ |
| 1.19.40 - 1.19.80 | Great component renaming; components stabilized out of experimental toggles. | Stable, widely supported |
| 1.20.20 - 1.20.60 | Built-in traits (`minecraft:placement_direction`), geometry bone visibility. | Stable |
| 1.20.80 - 1.21.x+ | JSON events removed; `minecraft:custom_components` links to Script API. | Current standard |
The 1.16.100 Experimental Era
Custom blocks gained most of their flexibility in the 1.16.100 update under the Holiday Creator Features experimental toggle. Prior to this, custom blocks were limited to simple unit cubes whose textures and render methods had to be registered inside the resource pack's root `blocks.json` file. Format version `1.16.100` moved visual definitions like `minecraft:geometry` and `minecraft:material_instances` directly into the behavior pack file.
This era also introduced block states, permutations, and an event system driven entirely by JSON (`minecraft:on_interact`, `minecraft:on_step_on`, and `minecraft:on_player_placing`). Many older code examples online still show this syntax. However, the JSON event architecture was never stabilized and has since been completely stripped from modern versions of Bedrock.
{
"format_version": "1.16.100",
"minecraft:block": {
"description": {
"identifier": "wiki:legacy_crate"
},
"components": {
"minecraft:destroy_time": 2.0,
"minecraft:explosion_resistance": 5.0,
"minecraft:unit_cube": {},
"minecraft:material_instances": {
"*": {
"texture": "legacy_crate_tex",
"render_method": "opaque"
}
}
}
}
}The 1.19.40 and 1.19.50 Stabilization Wave
During the 1.19 cycle, Mojang began moving block components out of experimental toggles and into the base game. Rather than keeping the 1.16.100 names as-is, many components were redesigned for clarity and consistency before being locked in as stable features. If you change a block's `format_version` from `1.16.100` to `1.19.40` or `1.19.50` without updating the component names, the block will fail to parse.
For example, `minecraft:destroy_time` became `minecraft:destructible_by_mining`, and `minecraft:explosion_resistance` became `minecraft:destructible_by_explosion`. The old `minecraft:unit_cube` component was retired in favor of using the standard block geometry identifier `minecraft:geometry.full_block` inside the `minecraft:geometry` component. Additionally, custom block properties inside the `description` block were standardized under the `states` key.
| Legacy Component (1.16.100) | Modern Replacement (1.19.40+) | Notes |
|---|---|---|
| minecraft:destroy_time | minecraft:destructible_by_mining | Now uses `seconds_to_destroy` inside an object or a boolean |
| minecraft:explosion_resistance | minecraft:destructible_by_explosion | Uses `explosion_resistance` field inside an object or a boolean |
| minecraft:block_light_emission | minecraft:light_emission | Takes an integer from 0 to 15 instead of a float from 0.0 to 1.0 |
| minecraft:block_light_absorption | minecraft:light_dampening | Takes an integer from 0 to 15 |
| minecraft:entity_collision | minecraft:collision_box | Uses `origin` and `size` arrays matching geometry coordinates |
| minecraft:pick_collision | minecraft:selection_box | Uses `origin` and `size` arrays matching geometry coordinates |
| minecraft:unit_cube | minecraft:geometry | Use identifier `minecraft:geometry.full_block` in `1.20.60+` |
Traits and Geometry Updates in 1.20
Across the 1.20 updates, the block format gained quality-of-life features that reduced repetitive boilerplate. In `1.20.20`, block traits were introduced under the `description` object. Traits like `minecraft:placement_direction` and `minecraft:placement_position` automatically inject standard cardinal, facing, or vertical-half states onto a block without requiring an external script or placement event to read the player's rotation.
The `minecraft:geometry` component also expanded during this period. Instead of creating multiple separate `.geo.json` files to toggle parts of a model on and off across permutations, creators can now define `bone_visibility` directly inside the `minecraft:geometry` component using booleans or Molang expressions, alongside the built-in `minecraft:geometry.full_block` identifier.
Removal of JSON Events and 1.21+ Custom Components
The largest breaking change in block format history occurred in 1.20.80 and 1.21.0. Mojang retired the Holiday Creator Features toggle and permanently removed JSON block events, event responses (`set_block_state`, `run_command`, `decrement_stack`), and all `minecraft:on_*` trigger components from the engine. Any pack relying on those components in newer versions will log content errors and ignore the triggers.
In their place, `1.21.0` introduced `minecraft:custom_components`. Instead of writing imperative logic inside JSON, you attach a named custom component to your block or permutation and register its behavior in JavaScript using the `@minecraft/server` API during world initialization.
{
"format_version": "1.21.0",
"minecraft:block": {
"description": {
"identifier": "wiki:modern_crate",
"menu_category": {
"category": "construction"
},
"traits": {
"minecraft:placement_direction": {
"enabled_states": ["minecraft:cardinal_direction"]
}
}
},
"components": {
"minecraft:geometry": "minecraft:geometry.full_block",
"minecraft:material_instances": {
"*": {
"texture": "modern_crate_tex",
"render_method": "opaque"
}
},
"minecraft:destructible_by_mining": {
"seconds_to_destroy": 2.0
},
"minecraft:destructible_by_explosion": {
"explosion_resistance": 5.0
},
"minecraft:custom_components": [
"wiki:crate_interaction"
]
}
}
}Which versions this applies to
Applies to all Bedrock versions from 1.16.0 through 1.21+. Modern blocks using format_version 1.21.0 and standard components require no experimental toggles.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| [Blocks][error]-wiki:my_block | minecraft:destroy_time is not a valid component | You bumped the file's `format_version` to `1.19.40` or higher, where the legacy `1.16.100` component names are no longer recognized by the schema parser. | Replace legacy component keys with their modern counterparts, such as changing `minecraft:destroy_time` to `minecraft:destructible_by_mining`. |
| Block loads into the world, but `minecraft:on_interact` or `events` do nothing and throw a Content Log error. | JSON block events and Holiday Creator Features were completely removed from the game engine starting in 1.20.80. | Update `format_version` to `1.21.0`, add `minecraft:custom_components` to the block, and move your interaction logic into a `@minecraft/server` script using `blockComponentRegistry.registerCustomComponent`. |
| Block appears as an update block (dirt with 'update!' text) or fails to appear in the creative inventory. | Your block file's `format_version` is set to a version higher than the `min_engine_version` specified in your behavior pack's `manifest.json`, or higher than the version of Minecraft you are currently running. | Ensure `format_version` is less than or equal to your current game version, and raise `min_engine_version` in `BP/manifest.json` to at least match the features you are using (for example, `[1, 21, 0]`). |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
