Why item JSON cannot do this alone
Since the removal of the legacy Holiday Creator Features experimental toggle in version 1.20.40, item behavior files no longer support the `events` block or the `run_command` event response. Modern custom items rely on stable components like `minecraft:wearable` and `minecraft:custom_components`. While custom components allow you to hook into specific moments like hitting an entity or using an item, there is no built-in item component that ticks every frame just because an item is worn or held.
Because the item itself stays silent while passively equipped, the detection logic must live on the player or in a global system. You define the wearable or holdable item in `BP/items/` as a normal data-driven item, and then let a function, an animation controller on `minecraft:player`, or a script watch the player's equipment slots and fire the commands.
{
"format_version": "1.20.80",
"minecraft:item": {
"description": {
"identifier": "my_pack:speed_boots",
"menu_category": {
"category": "equipment",
"group": "itemGroup.name.boots"
}
},
"components": {
"minecraft:max_stack_size": 1,
"minecraft:wearable": {
"slot": "slot.armor.feet",
"protection": 2
},
"minecraft:icon": "speed_boots"
}
}
}Equipment slot identifiers
All three detection methods require you to know how Bedrock names its equipment slots. Command selectors and Molang queries share the same string identifiers for armor and hands, while the Script API uses its own `EquipmentSlot` enumeration.
When configuring `minecraft:wearable` inside your item JSON or checking slots in commands and Molang, make sure the slot string matches the exact location where the item can be placed. Checking `slot.armor.chest` for an item that actually equips to `slot.armor.feet` is one of the easiest mistakes to overlook.
| Slot Location | Command & Molang String | Script API EquipmentSlot |
|---|---|---|
| Main hand | slot.weapon.mainhand | Mainhand |
| Offhand | slot.weapon.offhand | Offhand |
| Helmet | slot.armor.head | Head |
| Chestplate | slot.armor.chest | Chest |
| Leggings | slot.armor.legs | Legs |
| Boots | slot.armor.feet | Feet |
Method 1: Target selectors and tick.json
The fastest way to run commands for equipped items without editing `player.json` or writing JavaScript is a ticking mcfunction using the `hasitem` target selector argument. The `hasitem` filter accepts an `item` identifier and a `location` filter corresponding to the equipment slot. Because it runs as a standard selector argument, you can inline it directly inside `/effect`, `/execute`, or `/tag` commands.
To make this check run automatically, register an `.mcfunction` file inside `BP/functions/tick.json`. Keep in mind that `tick.json` runs 20 times per second. If you are applying status effects like speed or night vision, give the effect a duration of at least 2 seconds so the icon does not flash, or run the check on a scoreboard timer instead of every single tick.
{
"values": [
"equipment_checks"
]
}Writing the hasitem function
Inside `BP/functions/equipment_checks.mcfunction`, target `@a` with the `hasitem` argument. You can even check multiple slots at once by passing an array of filter objects to `hasitem` if you only want the command to run when a full armor set is worn.
If you need to run a one-time command the moment the player puts the item on—such as playing an equip sound or sending a chat message—combine `hasitem` with an entity tag. Run the setup command on players who have the item in the slot but lack the tag, add the tag immediately, and then strip the tag from players who have `quantity=0` in that slot.
# Continuous effect while boots are worn
effect @a[hasitem={item=my_pack:speed_boots,location=slot.armor.feet}] speed 2 1 true
# One-time trigger on equip using a tag
execute as @a[hasitem={item=my_pack:speed_boots,location=slot.armor.feet},tag=!wearing_speed_boots] at @s run playsound random.orb @s ~ ~ ~
tag @a[hasitem={item=my_pack:speed_boots,location=slot.armor.feet},tag=!wearing_speed_boots] add wearing_speed_boots
# Reset tag when unequipped
tag @a[hasitem={item=my_pack:speed_boots,location=slot.armor.feet,quantity=0},tag=wearing_speed_boots] remove wearing_speed_bootsMethod 2: Player behavior animation controllers
If your add-on already modifies `BP/entities/player.json`, you can use a behavior pack animation controller to run slash commands when an item is equipped. Unlike resource pack animation controllers, behavior pack animation controllers can include slash commands and entity events inside their `on_entry` and `on_exit` arrays.
Use the Molang query `query.is_item_name_any('slot.armor.feet', 0, 'my_pack:speed_boots')` in your transition condition. It returns `1.0` when the specified item is in slot index `0` of that equipment location, and `0.0` when it is removed. To hook the controller up, list it inside the `description.animations` and `description.scripts.animate` blocks of `BP/entities/player.json`. Be aware that modifying `player.json` makes your pack incompatible with any other add-on that also edits the player entity.
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.my_pack.speed_boots": {
"initial_state": "unequipped",
"states": {
"unequipped": {
"transitions": [
{
"equipped": "query.is_item_name_any('slot.armor.feet', 0, 'my_pack:speed_boots')"
}
]
},
"equipped": {
"on_entry": [
"/tellraw @s {\"rawtext\":[{\"text\":\"Speed Boots equipped!\"}]}"
],
"on_exit": [
"/effect @s speed 0"
],
"transitions": [
{
"unequipped": "!query.is_item_name_any('slot.armor.feet', 0, 'my_pack:speed_boots')"
}
]
}
}
}
}
}Method 3: Script API and entity equippable component
For modern add-ons that need to remain compatible with other packs, the `@minecraft/server` Script API is the cleanest choice. It does not require overriding `player.json`, and you can adjust the polling interval so you are not checking equipment 20 times a second if you do not need to.
In script, you retrieve the `minecraft:equippable` component from each player using `player.getComponent('minecraft:equippable')`. Calling `getEquipment(EquipmentSlot.Feet)` on that component returns an `ItemStack` if the slot is occupied, or `undefined` if it is empty. Once you verify `itemStack?.typeId`, you can either call `player.runCommand()` or use direct API methods like `player.addEffect()`.
import { world, system, EquipmentSlot } from "@minecraft/server";
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
const equippable = player.getComponent("minecraft:equippable");
if (!equippable) continue;
const feetItem = equippable.getEquipment(EquipmentSlot.Feet);
if (feetItem && feetItem.typeId === "my_pack:speed_boots") {
player.runCommand("effect @s speed 3 1 true");
}
}
}, 20);Which versions this applies to
Applies to Bedrock Edition 1.20.80 and newer. No experimental toggles are required for `minecraft:wearable`, the `hasitem` selector, behavior animation controllers, or the `minecraft:equippable` Script API component in stable `@minecraft/server` releases.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log Error: [Item][error]-my_pack:speed_boots | minecraft:wearable - does not match any allowed types | You used the old pre-1.20.20 syntax where `slot` was named `dispensable` or used an outdated format version that expects different field names under `minecraft:wearable`. | Set `format_version` to `1.20.80` or higher in your item JSON and use the `"slot": "slot.armor.feet"` property inside `minecraft:wearable`. |
| Syntax error: Unexpected "my_pack:speed_boots": at "hasitem={item=>>my_pack:speed_boots<<" | The custom item identifier is either misspelled, missing its namespace, or the behavior pack failed to load the item file due to a JSON syntax error. | Check the Content Log on world load to confirm the item registered cleanly, and ensure the `item=` value in `hasitem` includes the full `namespace:identifier`. |
| Behavior animation controller never transitions to the equipped state, with no errors in the Content Log. | You either used `query.get_equipped_item_name` (which is deprecated and only checks hands by non-standard names) or forgot to link the controller in `BP/entities/player.json` under both `description.animations` and `description.scripts.animate`. | Switch the condition to `query.is_item_name_any('slot.armor.feet', 0, 'my_pack:speed_boots')` and verify the short name is listed in `scripts.animate` in your behavior pack's `player.json`. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
