Wiki 9Minecraft Wiki 9Minecraft

Introduction to Custom Blocks

How to register, texture, and configure your first stable custom block in Minecraft Bedrock.

6Sections
4Code examples
3Known errors

Custom blocks allow you to add entirely new building materials, ores, and interactive machines to Minecraft Bedrock without replacing vanilla blocks. Unlike entities, which split their behavior and visuals neatly into separate component files, modern custom blocks often define both their gameplay rules and their rendering setup inside the Behavior Pack, while leaning on the Resource Pack for textures and localization. You reach for this system whenever you want a placeable object aligned to the world grid.

How Bedrock Splits Block Files

When you create a custom block, you work across both the Behavior Pack (BP) and the Resource Pack (RP). The Behavior Pack holds the main block definition inside the BP/blocks/ folder. One file per block is the standard convention. This JSON file assigns the block its unique identifier, puts it into the Creative inventory, and lists components that control hardness, collision, light emission, and even which geometry and textures to use.

The Resource Pack does not need a matching file per block. Instead, it uses a single master registry at RP/blocks.json (for vanilla-style cube blocks) and a texture atlas at RP/textures/terrain_texture.json. The terrain atlas assigns short string names to your actual PNG files so that either RP/blocks.json or the BP's material instances component can reference them without typing out full file paths.

The Behavior Pack Definition

Every custom block starts with a JSON file in BP/blocks/. At the top of the file, format_version tells the game which parser rules to use; stick to a modern version like 1.20.80 so you have access to stable components without needing experimental toggles. Inside minecraft:block, the description object sets the identifier and optionally places the block in the Creative menu.

Your identifier must have a namespace followed by a colon and the block name, all in lowercase. Never use minecraft as your namespace for custom blocks—the game rejects or breaks custom content that tries to register under the reserved vanilla namespace.

BP/blocks/slate_tile.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "my_pack:slate_tile",
      "menu_category": {
        "category": "construction",
        "group": "itemGroup.name.stoneBrick"
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 2.0
      },
      "minecraft:destructible_by_explosion": {
        "explosion_resistance": 6.0
      },
      "minecraft:light_emission": 0,
      "minecraft:map_color": "#3b3d42"
    }
  }
}

Core Gameplay Components

Without any components, a custom block uses default fallbacks: it breaks almost instantly, has a standard 16x16x16 unit collision box, and emits no light. You customize this behavior by adding entries to the components object. If you have looked at older packs, you might see names like minecraft:destroy_time or minecraft:block_light_emission; those were experimental names from the 1.16 to 1.18 era and have been replaced by stable equivalents.

The table below covers the foundational components you will put on almost every block you make.

ComponentWhat it doesNotes
minecraft:destructible_by_miningSets how long the block takes to break by hand, or makes it unbreakable.Pass a boolean false to make it unbreakable like bedrock, or an object with seconds_to_destroy.
minecraft:destructible_by_explosionControls whether TNT and creepers can destroy the block.Pass false to make it blast-proof, or an object with explosion_resistance.
minecraft:light_emissionMakes the block glow.Takes an integer from 0 to 15, just like vanilla light levels.
minecraft:light_dampeningControls how much sky and block light is blocked when passing through.Takes an integer from 0 (transparent like glass) to 15 (fully opaque).
minecraft:frictionControls how slippery the surface is when entities walk on it.Takes a float from 0.0 to 0.9. Default stone is 0.4; ice is 0.98.
minecraft:lootOverrides what items drop when the block is broken.Takes a string path relative to the BP root, such as "loot_tables/blocks/slate_tile.json". By default, a block drops itself.

Applying Textures and Sounds

There are two ways to texture a block in Bedrock. The first—and simplest for a standard six-sided cube—is to register the block inside RP/blocks.json in your Resource Pack. There you assign a sound preset and a texture shortname, either one for the whole block or an object specifying up, down, north, south, east, and west.

That shortname is then mapped to a real PNG file inside RP/textures/terrain_texture.json. Always omit the .png file extension in your JSON; the game resolves extensions automatically.

RP/blocks.json
{
  "format_version": "1.20.80",
  "my_pack:slate_tile": {
    "textures": "slate_tile_tex",
    "sound": "stone"
  }
}

Registering the Terrain Texture

Once RP/blocks.json knows the shortname slate_tile_tex, you must define that shortname inside RP/textures/terrain_texture.json. If your PNG is located at RP/textures/blocks/slate_tile.png, your atlas file looks like this.

If you later want a custom 3D model made in Blockbench instead of a plain cube, you can skip the textures line in RP/blocks.json and instead put the minecraft:geometry and minecraft:material_instances components directly inside your Behavior Pack block file. Even when you do that, you still register the texture shortname in terrain_texture.json the exact same way.

RP/textures/terrain_texture.json
{
  "resource_pack_name": "my_pack",
  "texture_name": "atlas.terrain",
  "padding": 8,
  "num_mip_levels": 4,
  "texture_data": {
    "slate_tile_tex": {
      "textures": "textures/blocks/slate_tile"
    }
  }
}

Naming the Block

When you load into a world and pull your new block out of the Creative menu, its hover tooltip will read tile.my_pack:slate_tile.name. Bedrock automatically builds this localization key by putting tile. in front of your full identifier and .name at the end.

To give it a clean display name, open or create RP/texts/en_US.lang and add a single line matching that key.

RP/texts/en_US.lang
tile.my_pack:slate_tile.name=Slate Tile

Which versions this applies to

Applies to Minecraft Bedrock 1.20.80 and newer. Everything shown on this page is part of the stable API and requires no experimental toggles on the world.

When it does not work

What you seeWhyThe fix
The block places in the world as a dirt block with the word "UPDATE!" written on every side.The game recognizes the block ID on the server side, or in a saved chunk, but the Behavior Pack definition failed to parse—usually because of a syntax error, a typo in a component name, or a format_version that does not support one of the components you used.Turn on the Content Log in Settings > Creator. Load the world and read the red error line naming your BP/blocks/ file to see which component or JSON line was rejected.
The block has a purple and black checkerboard texture in the inventory and when placed.The shortname you wrote in RP/blocks.json (or in minecraft:material_instances) does not match any key inside RP/textures/terrain_texture.json, or the file path inside terrain_texture.json points to a PNG that does not exist.Check that the shortname is spelled identically in both files, and verify the PNG is actually saved at the exact path listed under textures (with no .png extension in the JSON).
The block can be placed with /setblock, but it does not show up anywhere in the Creative inventory.Either the menu_category object was left out of description, or category was set to "none", or the group string is not a valid vanilla item group identifier.Add a menu_category block inside description with a valid category such as "construction", "nature", "equipment", or "items". If you are unsure of the group name, omit the group key entirely so the block lands at the bottom of the main category tab.

Reference, straight from the game files

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