When to Use Commands Instead of JSON Recipes
Standard behavior pack recipes located in the BP/recipes/ folder are always your first choice for normal survival crafting. They integrate cleanly with the recipe book, work natively inside the standard crafting grid, and require zero tick performance overhead. However, the native JSON recipe format has strict boundaries that force creators toward command-based alternatives.
First, JSON recipes cannot evaluate custom display names, enchantments, or lore on input items unless those variants are distinct custom item identifiers. If your map or add-on relies on vanilla items renamed at an anvil—such as a paper item named "Scroll of Fire"—a JSON recipe will treat it as ordinary paper. Second, JSON recipes cannot output items that already carry specific enchantments, custom names, or durability damage without hardcoding a bespoke custom item in the behavior pack. Third, JSON recipes are strictly bound to UI blocks; they cannot happen in the world on top of a custom altar block, nor can they check external conditions like the player's current scoreboard values, time of day, or surrounding multiblock structures.
When you hit any of these limitations, you have three standard command recipes to choose from: container grid scanning (using a dispenser or barrel as a faux crafting table), floor-drop crafting (throwing ingredients onto a specific block), or inventory-check crafting (triggering a craft via a button or NPC when the player holds the required items).
Pattern 1: Container Grid Scanning
The most reliable way to mimic a 3x3 shaped crafting grid without modifying UI files is to use a dispenser or dropper as your custom crafting station. Because dispensers have nine inventory slots indexed from 0 to 8, they map directly to a 3x3 crafting grid. Using the execute command's if blocks subcommand, you can compare the dispenser a player is standing near against a hidden template dispenser somewhere in a ticking area.
To set this up, place a template dispenser at a known, protected coordinate—for example, inside a bedrock box at 0 -60 0 kept loaded via the /tickingarea command. Fill its nine slots with the exact arrangement of items required for your recipe, including any custom names or enchantments on those items. In your behavior pack, run a tick function that checks if a dispenser near the player matches the template dispenser. Because facing direction matters in block comparisons, your template and the player's crafting dispenser must either face the same way (such as straight up), or you must test for all six orientations.
When the blocks match, your function replaces the contents of the player's dispenser with air (clearing the ingredients) and either spawns the output item directly into slot 4 (the center slot) using /replaceitem or clones a second "output template" dispenser into place. Cloning a second template is vastly superior if your crafted item has custom lore, enchantments, or a custom name, because /replaceitem can only generate base items without extra data.
# Run from an upward-facing dispenser located 1 block below the player
# Template input is at 0 -60 0, template output is at 0 -60 1
execute as @a at @s if block ~ ~-1 ~ dispenser["facing_direction"=1] if blocks ~ ~-1 ~ ~ ~-1 ~ 0 -60 0 all run tag @s add craft_success
execute as @a[tag=craft_success] at @s run clone 0 -60 1 0 -60 1 ~ ~-1 ~ replace
execute as @a[tag=craft_success] at @s run playsound random.anvil_use @a[r=8] ~ ~ ~
execute as @a[tag=craft_success] at @s run particle minecraft:villager_happy ~ ~-0.2 ~
tag @a[tag=craft_success] remove craft_successPattern 2: Floor-Drop Crafting
Floor crafting allows players to throw items onto a specific block in the world—like a cauldron, smithing table, or custom block—to combine them into a new item. This feels natural for magical rituals, forge mechanics, or quick field assembly. In Bedrock Edition, dropped item entities do not expose their item ID to standard entity type selectors (they are all simply minecraft:item), but you can filter them by their display name using the name selector argument.
Because the name argument matches the localized display name of the item, a dropped diamond is matched with @e[type=item,name="Diamond"]. Note that this makes floor crafting language-dependent unless you only match custom items whose localization strings you control across all language files, or items renamed to a fixed string. Furthermore, a single item entity can represent a stack of 1 to 64 items, and commands cannot easily read the stack size of a dropped item entity. If your recipe calls for one diamond and one stick, and a player drops a stack of 64 diamonds, killing the diamond entity will delete the entire stack.
To prevent players from losing full stacks, or to handle recipes comfortably, it is safer to combine floor triggers with the hasitem selector on the player, or to warn players to drop single items. However, for single-item unstackable inputs (like tools, potions, or enchanted books) combined with catalysts, floor crafting is clean and atmospheric. You tag the center ingredient, verify the secondary ingredient is within a tight radius and sitting on the correct block, spawn the output via /structure load, and kill the input entities.
# Anchor on an Iron Sword dropped on top of a crying obsidian block
execute as @e[type=item,name="Iron Sword"] at @s if block ~ ~-1 ~ crying_obsidian if entity @e[type=item,name="Blaze Powder",r=1] run tag @s add valid_ritual_base
# Spawn the custom output item saved in BP/structures/items/flame_blade.mcstructure
execute at @e[type=item,tag=valid_ritual_base] run structure load items:flame_blade ~ ~ ~
execute at @e[type=item,tag=valid_ritual_base] run particle minecraft:lava_particle ~ ~0.5 ~
execute at @e[type=item,tag=valid_ritual_base] run playsound mob.blaze.breathe @a[r=10] ~ ~ ~
# Clean up the ingredients (kills closest Blaze Powder stack and the base sword)
execute at @e[type=item,tag=valid_ritual_base] run kill @e[type=item,name="Blaze Powder",r=1,c=1]
kill @e[type=item,tag=valid_ritual_base]Pattern 3: Inventory Crafting with the hasitem Filter
If you want stack-safe crafting that works in any language and does not require placing template blocks in a ticking area, use the hasitem target selector argument. Introduced to give creators granular control over player and mob inventories, hasitem can check if an entity possesses a specific item identifier, a minimum or exact quantity, a specific data value, or an item in a specific slot location.
This pattern works best when attached to an explicit trigger—such as pressing a button on a wall, interacting with an NPC dialog, or sneaking while holding a catalyst item—rather than running passively every tick. If a recipe runs passively on every tick, a player might accidentally craft an item the moment they pick up the raw materials. By requiring the player to hold a specific item in their mainhand (location=slot.weapon.mainhand) while standing on a crafting block, you create an intentional trigger.
Once the selector confirms the player has all required quantities, you deduct the exact amounts using the /clear command, which takes an item name, data value, and max count to remove. Finally, you deliver the crafted result using /give for standard or custom behavior pack items, or /structure load at the player's feet if the output item carries custom NBT data like enchantments, lore, or durability modifications.
| Property | What it does | Notes |
|---|---|---|
| item | Specifies the namespace and ID of the item to check. | Required. The minecraft: prefix is optional for vanilla items. |
| quantity | Checks the total count of the item across all matched slots. | Supports ranges like 4.. (four or more), ..10 (up to ten), or !0 (any amount). |
| data | Filters by the item's auxiliary data or damage value. | Useful for checking tool durability; cannot differentiate custom NBT names. |
| location | Restricts the check to a specific inventory category. | Common values include slot.weapon.mainhand, slot.weapon.offhand, and slot.inventory. |
| slot | Checks a specific numbered slot within the chosen location. | Must be paired with location. Supports range notation like 0..8. |
# Player must stand on a smithing table and hold at least 1 Amethyst Shard in their mainhand
# Also requires 4 Copper Ingots and 2 Gold Ingots anywhere in their inventory
execute as @a[hasitem=[{item=amethyst_shard,location=slot.weapon.mainhand},{item=copper_ingot,quantity=4..},{item=gold_ingot,quantity=2..}]] at @s if block ~ ~-1 ~ smithing_table run tag @s add can_craft_alloy
# Deduct exact ingredient counts
clear @a[tag=can_craft_alloy] amethyst_shard 0 1
clear @a[tag=can_craft_alloy] copper_ingot 0 4
clear @a[tag=can_craft_alloy] gold_ingot 0 2
# Give the custom behavior pack item or load a structure
give @a[tag=can_craft_alloy] mypack:hepatizon_ingot 1
execute as @a[tag=can_craft_alloy] at @s run playsound random.SmithingTableUse @s ~ ~ ~
tag @a[tag=can_craft_alloy] remove can_craft_alloyDelivering Complex Outputs with Structure Files
A well-known limitation of Bedrock Edition's command engine is that /give and /replaceitem only accept a minimal JSON component object (strictly minecraft:can_place_on, minecraft:can_destroy, minecraft:item_lock, and minecraft:keep_on_death). You cannot use /give to hand a player an iron sword named "Excalibur" with Sharpness V and custom lore. If your custom crafting system exists specifically to create items with custom names, enchantments, or pre-filled shulker boxes, you must use structure files to spawn the output.
To create an item structure, drop your fully customized item onto the ground in a creative world. Run the /structure save command covering the single block space where the item is floating, making sure to set includeEntities to true and saveMode to disk, while excluding blocks. You can then copy that .mcstructure file from your world's db or structures folder into your behavior pack under BP/structures/items/. When a player completes a craft in your function, run /structure load at the player's coordinates (or above the crafting altar) to instantly spawn an exact clone of that dropped item entity.
To hook any of these crafting functions into an automated loop so they work without command blocks in the world, register your master crafting function inside BP/functions/tick.json. Keep the number of commands in tick.json small; if you have dozens of recipes, gate the check behind a proximity tag or only run the heavy checks every 5 to 10 ticks using a scoreboard timer.
{
"values": [
"crafting/check_altar_grid",
"crafting/floor_ritual",
"crafting/inventory_craft"
]
}Which versions this applies to
Applies to Bedrock Edition 1.19.50 and newer (utilizing the modern execute syntax and the hasitem selector argument introduced in 1.18.20). No experimental toggles are required for functions, structure loading, or command selectors.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Failed to execute 'blocks' as [Player] — The container crafting recipe never triggers even when items are placed in the exact 3x3 layout. | Either the chunk containing your template dispenser at 0 -60 0 is unloaded, or the player's dispenser has a different facing_direction block state than the template dispenser. The if blocks check requires the block states of the containers themselves to match. | Create a permanent ticking area around your template coordinates using /tickingarea add circle 0 -60 0 2 templates, and lock the orientation of the player's crafting block (for example, requiring upward-facing dispensers with ["facing_direction"=1]). |
| Syntax error: Unexpected "[": at "@a[hasitem=>>[<<{" inside the function log. | You nested brackets improperly or mixed single-condition syntax with array syntax in the hasitem selector, or the behavior pack manifest min_engine_version is set below 1.18.20. | When checking multiple items inside one hasitem filter, wrap the objects in square brackets like hasitem=[{item=apple},{item=stick}]. For a single item, omit the outer square brackets: hasitem={item=apple}. Ensure min_engine_version in manifest.json is at least [1, 19, 50]. |
| Floor crafting works in English, but stops detecting dropped items when a player switches their game language to Spanish or Japanese. | Target selectors using @e[type=item,name="..."] check the localized display string of the dropped item, not its technical identifier. | Switch from floor-drop detection to the inventory-based hasitem selector, which checks technical identifiers (such as minecraft:iron_sword) regardless of the client's language setting. |
| Function 'crafting/check_altar_grid' not found or commands using if/at fail to parse on world load. | Your commands use the new execute syntax (execute as ... at ... if ... run), but your behavior pack's manifest.json specifies an older min_engine_version, causing Bedrock to parse the .mcfunction files using the legacy pre-1.19.50 execute format. | Open BP/manifest.json and update header.min_engine_version to at least [1, 19, 50] so the function parser enables the modern execute subcommands. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
