Wiki 9Minecraft Wiki 9Minecraft

Item Event Migration to Custom Components

Updating legacy Holiday Creator Features item JSON events to the modern Script API custom component system.

5Sections
2Code examples
3Known errors

Item event migration is the process of converting older custom item files that rely on JSON-defined events and event responses into the modern Script API architecture. In Minecraft Bedrock Edition 1.20.80 and later, legacy JSON events for items were removed alongside the retirement of the Holiday Creator Features experimental toggle. You need this migration process whenever you are updating an older add-on to current format versions or building new interactive items that respond to player actions, block hits, or entity attacks.

Why Legacy Item Events Were Removed

In older versions of Bedrock Edition (such as format versions 1.16.100 through 1.20.40), creators built interactive items by chaining trigger components like minecraft:on_use or minecraft:on_use_on to an events block at the bottom of the item's behavior file. Inside that events block, creators used JSON event responses such as run_command, damage, decrement_stack, or teleport to make the item do something in the world.

Mojang removed this JSON event system for items and blocks to improve performance, reduce hard-to-debug Molang string evaluations, and unify custom logic under TypeScript and JavaScript. Starting in 1.21.0, any item with a format_version of 1.20.80 or higher will completely ignore an events block, and older trigger components will throw Content Log errors. Instead, all interactive behavior is now handled by registering a custom component in the Script API and attaching it to the item JSON via minecraft:custom_components.

Legacy vs Modern Architecture Mapping

When migrating an item, you do not translate the JSON line-for-line. Instead, you split your item into two parts: a clean, declarative JSON file that states what custom components the item uses, and a script file that listens for lifecycle hooks and executes the actual logic using the @minecraft/server module.

The table below outlines how the most common legacy JSON item triggers and event responses map to the modern Script API item custom component hooks and methods.

Legacy JSON ElementModern Script API Hook or MethodNotes
minecraft:on_useonUse(event)Fires when the player right-clicks or long-presses with the item in hand.
minecraft:on_use_ononUseOn(event)Fires when the item is used directly on a block face.
minecraft:weapon (on_hurt_entity)onHitEntity(event)Fires when an entity hits another entity while holding the item.
minecraft:digger (on_dig)onMineBlock(event)Fires when a player finishes breaking a block using the item.
minecraft:chargeable / foodonCompleteUse(event)Fires when an item finishes its use duration (requires minecraft:use_modifiers).
run_command responseEntity.runCommandAsync()Where possible, replace commands with direct API calls like Entity.addEffect().

Updating the Item Behavior JSON

To update your item's JSON file, begin by bumping the format_version to at least 1.20.80 (or 1.21.0 for stable release parity). Delete the entire events object from the bottom of the file, and remove any legacy trigger components such as minecraft:on_use from the components list.

In their place, add the minecraft:custom_components array. This array accepts custom namespaced strings that act as identifiers linking the JSON file to your JavaScript code. If your item used to rely on a cooldown or a use duration inside a legacy component, make sure you also include the modern equivalents, such as minecraft:cooldown and minecraft:use_modifiers.

BP/items/healing_staff.json
{
  "format_version": "1.21.0",
  "minecraft:item": {
    "description": {
      "identifier": "my_pack:healing_staff",
      "menu_category": {
        "category": "equipment"
      }
    },
    "components": {
      "minecraft:max_stack_size": 1,
      "minecraft:durability": {
        "max_durability": 25
      },
      "minecraft:cooldown": {
        "category": "healing_staff",
        "duration": 3.0
      },
      "minecraft:custom_components": [
        "my_pack:staff_heal_effect"
      ]
    }
  }
}

Registering the Component in the Script API

Once your JSON references your custom component identifier, you must register that identifier in your behavior pack's script before the world finishes loading. You do this by subscribing to the worldInitialize before-event (or system.beforeEvents.startup in the newest API releases) and calling registerCustomComponent on the itemComponentRegistry.

Inside the registration object, you define only the hooks your item actually needs. In the example below, we migrate a legacy item that used to run a regeneration effect command, play a sound, and take durability damage when right-clicked.

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

world.beforeEvents.worldInitialize.subscribe((initEvent) => {
  initEvent.itemComponentRegistry.registerCustomComponent(
    "my_pack:staff_heal_effect",
    {
      onUse(event) {
        const player = event.source;
        if (!player) return;

        // Replaces legacy add_mob_effect or run_command event response
        player.addEffect("regeneration", 100, { amplifier: 1 });
        player.dimension.playSound("random.orb", player.location);

        // Skip durability loss in Creative mode
        if (player.getGameMode() === GameMode.creative) return;

        // Replaces legacy "damage" JSON event response
        const equippable = player.getComponent("minecraft:equippable");
        if (!equippable) return;

        const itemStack = equippable.getEquipment(EquipmentSlot.Mainhand);
        if (!itemStack) return;

        const durability = itemStack.getComponent("minecraft:durability");
        if (durability) {
          if (durability.damage + 1 >= durability.maxDurability) {
            equippable.setEquipment(EquipmentSlot.Mainhand, undefined);
            player.dimension.playSound("random.break", player.location);
          } else {
            durability.damage += 1;
            equippable.setEquipment(EquipmentSlot.Mainhand, itemStack);
          }
        }
      }
    }
  );
});

Migrating Stack Decrement and Item Replacement

Two of the most heavily used legacy JSON responses were decrement_stack (which consumed one item from the player's hand) and transform_item (which replaced the used item with an empty bucket or bottle). Because ItemStack instances in the Script API are value copies rather than live references to the inventory slot, modifying event.itemStack directly will not update the player's hand until you write the modified stack back to the equipment component.

When migrating a consumable item that does not use the standard food component, retrieve the player's minecraft:equippable component, reduce the amount property of the ItemStack by 1 (or set the slot to undefined if the amount reaches 0), and call setEquipment(EquipmentSlot.Mainhand, itemStack) to commit the change. Always check whether the player is in Creative mode before decrementing stacks or applying durability damage, as legacy JSON events often handled Creative mode exemptions automatically via an ignore_game_mode flag.

Which versions this applies to

Applies to Bedrock Edition 1.20.80 and 1.21.0+. Custom components are stable and do not require any experimental toggles in 1.21.0 and later, provided your manifest.json declares a stable @minecraft/server dependency (version 1.11.0 or higher).

When it does not work

What you seeWhyThe fix
[Item][error]-my_pack:healing_staff | minecraft:on_use is not a valid componentYou raised the item's format_version to 1.20.80 or higher, where legacy JSON event triggers have been stripped from the schema, or you left legacy components in the file after disabling Holiday Creator Features.Remove minecraft:on_use, minecraft:on_use_on, and the events block from your item JSON, and replace them with minecraft:custom_components.
[Item][error]-my_pack:healing_staff | Failed to find custom component 'my_pack:staff_heal_effect'The game parsed the item JSON during world startup, but no script registered a custom component with that exact identifier before initialization finished.Verify that your manifest.json has a valid script entry point, that the identifier string matches in both JSON and JS, and that registerCustomComponent is called inside world.beforeEvents.worldInitialize (or system.beforeEvents.startup) rather than inside an after-event.
Durability damage or stack reduction executes in the script without errors, but the item in the player's hand never actually changes.Modifying properties on an ItemStack or its minecraft:durability component only alters the copy held in script memory, not the actual slot in the player's inventory.After changing durability.damage or itemStack.amount, pass the modified ItemStack back into the player's hand using equippable.setEquipment(EquipmentSlot.Mainhand, itemStack).

Reference, straight from the game files

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