Wiki 9Minecraft Wiki 9Minecraft

Custom Crops

Build multi-stage growing plants with states, permutations, random ticks, and bonemeal support.

5Sections
4Code examples
3Known errors

Custom crops are non-solid blocks that advance through visual growth stages over time and drop different loot when mature. In modern Bedrock add-ons, you build them by combining an integer block state, permutations for textures and hitboxes, and a Script API custom component to handle random ticks and bonemeal interactions. You reach for this pattern whenever you want farmable plants, berry bushes, or spreading flora.

Architecture of a Bedrock crop

A working crop requires four distinct pieces working together across your behavior pack (BP) and resource pack (RP). First, the block definition declares an integer state (for example, wiki:growth_stage from 0 to 3) and uses permutations to swap textures, loot tables, and selection boxes at each stage. Second, a plant-style geometry—either the built-in cross model or a custom Blockbench model—renders flat planes with transparency.

Third, because the legacy JSON event system was removed in format version 1.20.80, growth logic now lives in TypeScript or JavaScript using the @minecraft/server module. You register a block custom component that listens to onRandomTick to increment the state naturally, and onPlayerInteract to advance the state when the player holds bonemeal. Finally, two separate loot tables handle what drops when a player breaks an immature sprout versus a fully grown plant.

Component / FeatureWhat it doesNotes
statesTracks the current growth stage as an integer on the blockDefined in the description object; maximum 16 total state permutations per trait
minecraft:placement_filterRestricts placement to farmland or dirt and pops the crop off if the soil breaksChecks the block directly below using block_filter
minecraft:collision_boxControls physical collision with entitiesSet to false so players and mobs walk straight through the crop
minecraft:selection_boxDefines the wireframe outline players target to break or interactUsually grows taller in later permutations
minecraft:custom_componentsBinds Script API listeners for random ticks and player interactionStable since 1.21.10; replaces old Holiday Creator Features events

Behavior pack block definition

Start by creating the block JSON in your behavior pack. To make the block look and act like a plant, disable collision, set the render method to alpha_test so transparent pixels on your texture are cut out cleanly, and make it instant-break with minecraft:destructible_by_mining. The identifier minecraft:geometry.cross is built into vanilla Bedrock and arranges two intersecting planes diagonally across the block space, saving you from having to supply a custom geometry file for simple crops.

Use permutations to override the material instances, selection box height, and loot table at each growth stage. Notice how stages 0 through 2 share a seed loot table on the base components block, while stage 3 overrides minecraft:loot in its permutation to point to the mature harvest table. The placement filter ensures players can only plant the crop on top of vanilla farmland.

BP/blocks/tomato_crop.json
{
  "format_version": "1.21.10",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:tomato_crop",
      "states": {
        "wiki:growth": [0, 1, 2, 3]
      }
    },
    "components": {
      "minecraft:collision_box": false,
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 0.0
      },
      "minecraft:destructible_by_explosion": {
        "explosion_resistance": 0.0
      },
      "minecraft:geometry": "minecraft:geometry.cross",
      "minecraft:light_dampening": 0,
      "minecraft:loot": "loot_tables/blocks/tomato_crop_seeds.json",
      "minecraft:placement_filter": {
        "conditions": [
          {
            "allowed_faces": ["up"],
            "block_filter": ["minecraft:farmland"]
          }
        ]
      },
      "minecraft:custom_components": [
        "wiki:crop_growth"
      ]
    },
    "permutations": [
      {
        "condition": "q.block_state('wiki:growth') == 0",
        "components": {
          "minecraft:selection_box": {
            "origin": [-6, 0, -6],
            "size": [12, 4, 12]
          },
          "minecraft:material_instances": {
            "*": {
              "texture": "tomato_stage_0",
              "render_method": "alpha_test",
              "face_dimming": false,
              "ambient_occlusion": false
            }
          }
        }
      },
      {
        "condition": "q.block_state('wiki:growth') == 1",
        "components": {
          "minecraft:selection_box": {
            "origin": [-6, 0, -6],
            "size": [12, 8, 12]
          },
          "minecraft:material_instances": {
            "*": {
              "texture": "tomato_stage_1",
              "render_method": "alpha_test",
              "face_dimming": false,
              "ambient_occlusion": false
            }
          }
        }
      },
      {
        "condition": "q.block_state('wiki:growth') == 2",
        "components": {
          "minecraft:selection_box": {
            "origin": [-6, 0, -6],
            "size": [12, 12, 12]
          },
          "minecraft:material_instances": {
            "*": {
              "texture": "tomato_stage_2",
              "render_method": "alpha_test",
              "face_dimming": false,
              "ambient_occlusion": false
            }
          }
        }
      },
      {
        "condition": "q.block_state('wiki:growth') == 3",
        "components": {
          "minecraft:loot": "loot_tables/blocks/tomato_crop_mature.json",
          "minecraft:selection_box": {
            "origin": [-6, 0, -6],
            "size": [12, 16, 12]
          },
          "minecraft:material_instances": {
            "*": {
              "texture": "tomato_stage_3",
              "render_method": "alpha_test",
              "face_dimming": false,
              "ambient_occlusion": false
            }
          }
        }
      }
    ]
  }
}

Growth and bonemeal via Script API

To make the crop actually advance through its wiki:growth state, register the wiki:crop_growth custom component during the worldInitialize before-event in your behavior pack script. Every block with an onRandomTick handler is automatically queued by the game engine to receive random ticks at the same rate as vanilla crops, without needing any extra ticking component in the block JSON.

In the onPlayerInteract callback, inspect the item in the player's mainhand using EntityEquippableComponent. If the item is minecraft:bone_meal and the growth state is below the maximum of 3, apply the new permutation with BlockPermutation.withState, spawn the vanilla happy villager particle at the block's center, and decrement the item stack if the player is not in Creative mode.

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

world.beforeEvents.worldInitialize.subscribe((event) => {
  event.blockComponentRegistry.registerCustomComponent("wiki:crop_growth", {
    onRandomTick(e) {
      const growth = e.block.permutation.getState("wiki:growth");
      if (typeof growth === "number" && growth < 3) {
        if (Math.random() < 0.35) {
          e.block.setPermutation(e.block.permutation.withState("wiki:growth", growth + 1));
        }
      }
    },
    onPlayerInteract(e) {
      const { block, player, dimension } = e;
      if (!player) return;

      const growth = block.permutation.getState("wiki:growth");
      if (typeof growth !== "number" || growth >= 3) return;

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

      block.setPermutation(block.permutation.withState("wiki:growth", growth + 1));
      dimension.spawnParticle("minecraft:crop_growth_emitter", block.center());
      dimension.playSound("item.bone_meal.use", block.center());

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

Seeds item and terrain textures

Players normally plant crops from a seed item rather than placing the block item directly. Create a custom item with the minecraft:block_placer component configured to place wiki:tomato_crop on top of farmland. Setting replace_block_item to true only makes sense if the item shares the exact same identifier as the block; for a separate seed item, leave that out and let the block's seed loot table drop your custom seed identifier.

In your resource pack, map each short texture name used in minecraft:material_instances (tomato_stage_0 through tomato_stage_3) inside RP/textures/terrain_texture.json. Make sure your PNG files have a transparent background, not solid white or black, so the alpha_test render method cuts out the empty space around the leaves.

BP/items/tomato_seeds.json
{
  "format_version": "1.21.10",
  "minecraft:item": {
    "description": {
      "identifier": "wiki:tomato_seeds",
      "menu_category": {
        "category": "nature",
        "group": "itemGroup.name.seed"
      }
    },
    "components": {
      "minecraft:icon": "tomato_seeds",
      "minecraft:block_placer": {
        "block": "wiki:tomato_crop",
        "use_on": [
          "minecraft:farmland"
        ]
      }
    }
  }
}

Harvest loot tables

A crop needs two loot tables in BP/loot_tables/blocks/: one for immature stages that simply returns a single seed, and one for the final permutation that drops both the harvested produce and one to three seeds. Putting the immature loot table on the base components list and overriding it only in the final permutation keeps the block JSON compact.

BP/loot_tables/blocks/tomato_crop_mature.json
{
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "item",
          "name": "wiki:tomato",
          "functions": [
            {
              "function": "set_count",
              "count": {
                "min": 2,
                "max": 4
              }
            }
          ]
        }
      ]
    },
    {
      "rolls": 1,
      "entries": [
        {
          "type": "item",
          "name": "wiki:tomato_seeds",
          "functions": [
            {
              "function": "set_count",
              "count": {
                "min": 1,
                "max": 3
              }
            }
          ]
        }
      ]
    }
  ]
}

Which versions this applies to

Applies to Minecraft Bedrock 1.21.10 and newer. No experimental toggles are required; minecraft:custom_components, minecraft:geometry.cross, and the worldInitialize registry in @minecraft/server (1.12.0+) are fully stable.

When it does not work

What you seeWhyThe fix
Content Log Error: [Blocks][error]-wiki:tomato_crop | minecraft:custom_components - failed to find custom component 'wiki:crop_growth'Your BP/manifest.json is missing the script module dependency for @minecraft/server, the entry script path is wrong, or registerCustomComponent was called outside the world.beforeEvents.worldInitialize listener.Add the "script" module and "@minecraft/server" dependency to BP/manifest.json, verify the entry file path matches BP/scripts/main.js, and register the component inside worldInitialize.
The crop renders with black or opaque white squares filling the empty space around the plant.The default render_method on minecraft:material_instances is opaque, which ignores the alpha channel of your PNG texture.Set "render_method": "alpha_test" inside every material instance in your block's permutations, and ensure "minecraft:light_dampening": 0 is set on the base components so the block does not cast a dark shadow on itself.
The crop looks abnormally dark on one side or shades the farmland underneath it black.Cross-plane geometry calculates standard block face lighting and ambient occlusion unless told otherwise.Add "face_dimming": false and "ambient_occlusion": false to the "*" material instance in each permutation, alongside "minecraft:light_dampening": 0.

Reference, straight from the game files

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