Wiki 9Minecraft Wiki 9Minecraft

Generating Custom Ores

How to place custom ore blocks underground using features and feature rules.

5Sections
2Code examples
3Known errors

Adding a custom ore to a Minecraft Bedrock add-on is a two-step world generation process: first you define the cluster shape as a feature, and then you tell the game where and how often to place that cluster using a feature rule. You reach for this system whenever you want a custom block to generate naturally inside stone, deepslate, or netherrack as players explore new chunks.

How Bedrock Ore Generation Works

Minecraft Bedrock separates world generation into two distinct folders inside the behaviour pack: features and feature_rules. Understanding the split between these two folders is the most important part of getting custom ores to appear in a world.

A feature file defines the physical structure being placed. For an ore, you use the minecraft:ore_feature type, which creates a blob of blocks of a given size and replaces specific target blocks like stone or deepslate. The feature itself has no idea where in the world it belongs; it only knows what blocks it is made of and how many blocks are in a single cluster.

A feature rule file controls placement. It picks a feature by its identifier, filters which biomes the feature is allowed to spawn in, sets the number of placement attempts per chunk, and defines the X, Y, and Z coordinates for each attempt. If your ore block works in the creative inventory but never shows up underground, the issue is almost always in the linkage between these two files or in the coordinate distribution of the feature rule.

Prerequisite: The Custom Ore Block

World generation files do not create blocks; they only reference block identifiers that already exist. Before writing a feature or a feature rule, you need a working custom block registered in your behaviour pack's blocks folder, along with its textures and localization in your resource pack.

For the examples on this page, assume you already have a custom block with the identifier wiki:ruby_ore. If you want your ore to generate deep underground where stone transitions to deepslate, it is good practice to register a second block, such as wiki:deepslate_ruby_ore, so the single ore feature can swap between them seamlessly based on what block it is replacing.

Creating the Ore Feature

Place your ore feature inside the BP/features/ folder. A standard rule of thumb in Bedrock is that the filename (without the .json extension) must match the identifier inside the description block. If your identifier is wiki:ruby_ore_feature, name the file ruby_ore_feature.json. You can organize features into subfolders if your pack is large, as long as the base filename matches the identifier name.

Inside minecraft:ore_feature, the count property sets the approximate maximum number of blocks in a single vein. The replace_rules array lists which custom block should be placed and which existing blocks it is allowed to overwrite. By supplying multiple rules in this array, a single vein straddling the boundary between normal stone and deepslate will naturally convert into both variants of your ore.

FieldWhat it doesNotes
identifierUnique name for this featureMust match the filename without .json
countNumber of blocks in one ore clusterValues under 4 often produce 1-2 blocks or nothing at all
places_blockThe block identifier to placeCan be a string or an object specifying block states
may_replaceArray of blocks this ore can overwriteIf the target coordinate has a block not in this list, placement fails silently
BP/features/ruby_ore_feature.json
{
  "format_version": "1.13.0",
  "minecraft:ore_feature": {
    "description": {
      "identifier": "wiki:ruby_ore_feature"
    },
    "count": 8,
    "replace_rules": [
      {
        "places_block": "wiki:ruby_ore",
        "may_replace": [
          "minecraft:stone",
          "minecraft:granite",
          "minecraft:diorite",
          "minecraft:andesite"
        ]
      },
      {
        "places_block": "wiki:deepslate_ruby_ore",
        "may_replace": [
          "minecraft:deepslate",
          "minecraft:tuff"
        ]
      }
    ]
  }
}

Creating the Feature Rule

Next, create a file in BP/feature_rules/ to attach the feature to the world generator. Just like features, the filename should match the identifier inside the description block. In the description, places_feature must point to the exact identifier of the ore feature you created in the previous step.

Inside conditions, the placement_pass string tells the engine when during chunk generation this rule runs. Ores always use the underground_pass so they generate after stone and caves are carved out, but before late-stage decorations. The minecraft:biome_filter uses standard filter tests against biome tags; checking for the overworld or overworld_generation tag ensures your ore spawns across normal Overworld biomes without accidentally trying to generate in the Nether or the End.

The distribution object determines how many veins try to spawn per chunk (iterations) and picks the coordinates for each attempt. For X and Z, a uniform distribution from 0 to 16 covers the entire 16x16 horizontal area of a chunk. For Y, you can use uniform for an even spread between two heights, or triangle to make the ore most common at the midpoint between your minimum and maximum heights.

FieldWhat it doesNotes
places_featureLinks the rule to a feature identifierMust match the identifier in BP/features/
placement_passGeneration stage for this ruleUse underground_pass for standard ores
iterationsPlacement attempts per chunkAccepts an integer or a Molang expression
distributionShape of the random number curveCommon values are uniform and triangle
extentMinimum and maximum coordinate boundsX and Z should be [0, 16]; Y can go down to -64 in the Overworld
BP/feature_rules/overworld_underground_ruby_ore_feature.json
{
  "format_version": "1.13.0",
  "minecraft:feature_rules": {
    "description": {
      "identifier": "wiki:overworld_underground_ruby_ore_feature",
      "places_feature": "wiki:ruby_ore_feature"
    },
    "conditions": {
      "placement_pass": "underground_pass",
      "minecraft:biome_filter": [
        {
          "any_of": [
            {
              "test": "has_biome_tag",
              "operator": "==",
              "value": "overworld"
            },
            {
              "test": "has_biome_tag",
              "operator": "==",
              "value": "overworld_generation"
            }
          ]
        }
      ]
    },
    "distribution": {
      "iterations": 10,
      "x": {
        "distribution": "uniform",
        "extent": [ 0, 16 ]
      },
      "y": {
        "distribution": "triangle",
        "extent": [ -64, 32 ]
      },
      "z": {
        "distribution": "uniform",
        "extent": [ 0, 16 ]
      }
    }
  }
}

Testing Your Ore Generation

World generation only runs when a chunk is created for the very first time. If you add a feature rule to an existing test world, your ore will never appear in the chunks you have already visited. Always create a fresh world or teleport thousands of blocks away into unvisited terrain when testing changes to features or feature rules.

Because hunting for rare ores by hand is slow, temporarily crank the iterations value in your feature rule up to 50 or 100 while testing, or use the /fill command in creative mode to clear out large volumes of stone and deepslate around you. For example, running /fill ~-15 ~-10 ~-15 ~15 ~10 ~15 air replace stone will strip away the normal stone and leave your custom ore veins floating in mid-air so you can immediately verify their size and height distribution.

Which versions this applies to

Format version 1.13.0 for features and feature_rules is stable and works in current Bedrock releases (1.20 and 1.21+) without any experimental toggles enabled. Make sure your world's Caves and Cliffs generation height (-64 to 320) is taken into account if you use negative Y coordinates.

When it does not work

What you seeWhyThe fix
Content Log shows an error stating the feature identifier does not match the filename, and no ores generate.Bedrock enforces a strict naming convention on files in BP/features/ and BP/feature_rules/. The string after the namespace colon in the identifier must be identical to the file's name on disk.If your identifier is "wiki:ruby_ore_feature", rename the file to exactly ruby_ore_feature.json.
No errors appear in the Content Log, the custom block works via /setblock, but the ore never generates underground.Either the Y extent is set to heights where the blocks in may_replace do not exist (for example, only listing minecraft:stone while setting the Y extent deep in the deepslate layer between -64 and -8), or you are checking chunks that were generated before the pack was active.Include both minecraft:stone and minecraft:deepslate in your replace_rules for negative Y levels, and generate a brand-new world to test.
Content Log reports "Failed to load feature rule" or complains about an unrecognised block in replace_rules.A typo in the block identifier inside places_block or may_replace, or the custom block itself failed to load due to an error in BP/blocks/.Check that every block identifier in your feature matches the exact identifier in BP/blocks/, including the namespace, and fix any block errors listed earlier in the Content Log.

Reference, straight from the game files

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