Wiki 9Minecraft Wiki 9Minecraft

Giving NBT Items and Custom Data

How to give players items with components, custom lore, names, and data in Minecraft Bedrock.

6Sections
6Code examples
4Known errors

Unlike Java Edition, Minecraft Bedrock does not allow raw NBT strings inside the /give or /replaceitem commands. Instead, Bedrock exposes four specific item components directly in command syntax, while complex properties like custom names, lore, enchantments, and dynamic data must be applied through loot tables, structure blocks, or the Script API. You reach for these techniques whenever your add-on or map needs to hand out adventure-mode tools, named quest items, or gear with persistent custom data.

The Native Command JSON Components

When you run the /give or /replaceitem command in Bedrock, the final argument is an optional JSON object. This JSON object is often mistaken for full NBT access, but the command parser only recognizes four specific keys inside it. If you try to pass Java-style keys like display, Enchantments, or Unbreakable, the command will fail with a syntax error.

The four supported components are built primarily for Adventure mode mapmakers and basic inventory management. They let you lock items into a player's inventory, stop items from being dropped on death, or allow specific tools and blocks to work when the player is in Adventure mode (where breaking and placing blocks is normally disabled).

Because the command parser expects strict JSON, every key and string value must be wrapped in double quotes. When typing these commands into chat, command blocks, or .mcfunction files, make sure your quotation marks are not accidentally converted into curly smart quotes by your text editor.

Component KeyWhat it doesNotes
minecraft:can_place_onAllows the block item to be placed on the listed block identifiers while in Adventure mode.Takes a "blocks" array of block identifier strings (with or without the minecraft: namespace).
minecraft:can_destroyAllows the item to break the listed block identifiers while in Adventure mode.Takes a "blocks" array of block identifier strings.
minecraft:item_lockPrevents the item from being dropped, moved, or crafted away.Requires a "mode" string: either "lock_in_inventory" (can move slots, cannot drop) or "lock_in_slot" (cannot move or drop).
minecraft:keep_on_deathKeeps the item in the player's inventory when they die, even if the keepInventory gamerule is false.Takes an empty JSON object: {}.
BP/functions/give_adventure_tools.mcfunction
# Give an iron pickaxe that can break gold and diamond ore in Adventure mode
give @s iron_pickaxe 1 0 {"minecraft:can_destroy":{"blocks":["gold_ore","diamond_ore"]}}

# Give redstone wire that can only be placed on stone, and stays in inventory on death
give @s redstone 16 0 {"minecraft:can_place_on":{"blocks":["stone"]},"minecraft:keep_on_death":{}}

# Put a locked helmet directly onto the player's head slot
replaceitem entity @s slot.armor.head 0 golden_helmet 1 0 {"minecraft:item_lock":{"mode":"lock_in_slot"}}

Giving Named and Enchanted Items via Loot Tables

If you want to give an item with a custom display name, lore lines, specific enchantments, or preset durability without using scripts, the standard pure-JSON method is a custom loot table paired with the /loot command. Loot tables reside in your behaviour pack under the BP/loot_tables/ directory and have access to loot functions that the /give command lacks.

Inside a loot table entry, the set_name function changes the item's hover name, and the set_lore function accepts an array of strings to display beneath the name. You can include section-sign formatting codes (such as §6 for gold or §r to reset formatting) directly inside these strings to style the text.

Once your loot table is saved in your behaviour pack, you trigger it in-game or from an .mcfunction file using /loot give. Notice that the path you pass to the /loot command omits the leading BP/loot_tables/ folder and the trailing .json file extension.

BP/loot_tables/items/dungeon_key.json
{
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "item",
          "name": "minecraft:tripwire_hook",
          "functions": [
            {
              "function": "set_count",
              "count": 1
            },
            {
              "function": "set_name",
              "name": "§r§eRusty Crypt Key"
            },
            {
              "function": "set_lore",
              "lore": [
                "§r§7Opens the gate on B2.",
                "§r§8Single use only"
              ]
            },
            {
              "function": "specific_enchants",
              "enchants": [
                {
                  "id": "unbreaking",
                  "level": 1
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Triggering the Loot Table in Functions

To actually hand the loot table item to a player, use the /loot give or /loot replace command. Unlike /give, which outputs a chat message by default unless commandBlockOutput or sendCommandFeedback is disabled, /loot silently inserts the generated items into the target's inventory.

If you need the item to go into a specific armor slot, offhand slot, or hotbar index rather than the first available slot, use /loot replace entity instead of /loot give. This acts just like /replaceitem, overwriting whatever is currently in that slot with the output of your loot table.

BP/functions/grant_key.mcfunction
# Give the custom key into the first open inventory slot
loot give @s loot "items/dungeon_key"

# Alternatively, put the key directly into the player's offhand
loot replace entity @s slot.weapon.offhand 0 loot "items/dungeon_key"

Giving Exact NBT Items with Structure Blocks

Loot tables cannot generate every possible item state in the game, such as written books with multiple pages, dyed leather armor with exact RGB values, fireworks with complex flight stars, or shulker boxes pre-filled with custom items. When you need an exact 1:1 clone of an in-game item's internal NBT data, the structure method is the most reliable solution.

This technique works because structure files (.mcstructure) save entities and dropped item entities along with their complete Bedrock NBT tree. By saving a dropped item entity inside a 1x1x1 structure without any blocks, you can load that structure directly at a player's coordinates. Because the item entity is saved at the exact moment it can be picked up, loading the structure at the player's feet immediately places the item into their inventory.

To set this up in your world: create the exact item you want (using an anvil, enchanting table, dye cauldron, or external NBT editor). Stand inside a 1x1x1 structure bounding box, throw the item on the ground, wait one second so its pickup delay expires, and save the structure using a Structure Block with 'Include Entities' turned ON and 'Remove Blocks' turned ON. Export the .mcstructure file into your behaviour pack's BP/structures/ folder.

BP/functions/give_saved_item.mcfunction
# Execute at the target player and load the entity-only structure at their feet
# Arguments: <name: string> <to: x y z> [rotation] [mirror] [includeEntities: Boolean] [includeBlocks: Boolean]
execute at @s run structure load "mystructure:custom_book" ~ ~ ~ 0_degrees none true false

Constructing Rich Items with the Script API

For modern add-ons using @minecraft/server, the Script API offers the most flexible and dynamic way to create and give NBT-like items. Using the ItemStack class, you can set custom nameTags, dynamically build lore arrays based on player scores or stats, apply enchantments, and attach true custom key-value data using dynamic properties.

Unlike the four command components, you can configure ItemStack properties in JavaScript at runtime. You can also set the exact same Adventure mode arrays (canDestroy and canPlaceOn), as well as the lockMode and keepOnDeath boolean directly on the ItemStack instance before inserting it into the player's inventory container.

Note that dynamic properties set on an ItemStack via setDynamicProperty only work on non-stackable items (items with a maximum stack size of 1, such as swords, pickaxes, or custom items with minecraft:max_stack_size set to 1). Attempting to set a dynamic property on a stackable item like apples or cobblestones will throw an exception at runtime.

BP/scripts/give_custom_sword.js
import { world, system, ItemStack, ItemLockMode, EnchantmentType } from "@minecraft/server";

function giveCustomSword(player) {
  const sword = new ItemStack("minecraft:diamond_sword", 1);

  // Set custom display name and lore
  sword.nameTag = "§r§bFrostbite Blade";
  sword.setLore([
    "§r§7Forged in the northern ice spikes.",
    "§r§9+10 Freeze Damage"
  ]);

  // Apply command-equivalent properties
  sword.keepOnDeath = true;
  sword.lockMode = ItemLockMode.lockInInventory;
  sword.setCanDestroy(["minecraft:ice", "minecraft:packed_ice", "minecraft:blue_ice"]);

  // Attach custom NBT-like data (only allowed on max stack size 1 items)
  sword.setDynamicProperty("myaddon:weapon_tier", 3);
  sword.setDynamicProperty("myaddon:owner_id", player.id);

  // Apply vanilla enchantments via the enchantable component
  const enchantable = sword.getComponent("minecraft:enchantable");
  if (enchantable) {
    enchantable.addEnchantment({
      type: new EnchantmentType("sharpness"),
      level: 4
    });
  }

  // Add the constructed item to the player's inventory
  const inventory = player.getComponent("minecraft:inventory");
  if (inventory && inventory.container) {
    inventory.container.addItem(sword);
  }
}

// Example trigger: give the sword when a player sends a specific scriptevent
system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id === "myaddon:give_sword" && event.sourceEntity?.typeId === "minecraft:player") {
    giveCustomSword(event.sourceEntity);
  }
});

Detecting Custom Items in Commands

Once you have given a player a custom item, you frequently need to check if they are holding it or carrying it in their inventory. In Bedrock commands, the hasitem target selector argument lets you filter entities by item identifier, data value, quantity, location slot, and slot index.

However, the hasitem selector argument cannot read custom names, lore, enchantments, or dynamic properties. If you give a player a named tripwire hook via a loot table and check for hasitem={item=tripwire_hook}, any ordinary tripwire hook will also pass the check.

If you want pure command detection without scripts, the best practice is to create a dedicated custom item in your behaviour pack (for example, myaddon:dungeon_key) rather than renaming a vanilla item. If you are already using the Script API, you can inspect the player's mainhand slot using the EntityEquippableComponent and verify the item's nameTag, getLore(), or getDynamicProperty() values in JavaScript.

BP/functions/check_held_item.mcfunction
# Check if the player is holding a custom item identifier in their mainhand
execute as @a[hasitem={item=myaddon:dungeon_key,location=slot.weapon.mainhand}] run say I am holding the dungeon key!

# Check if the player has at least 4 of an item anywhere in their inventory
execute as @a[hasitem={item=myaddon:ancient_coin,quantity=4..}] run tag @s add can_afford_upgrade

Which versions this applies to

Applies to Minecraft Bedrock 1.19.50 and newer (1.20.x, 1.21.x). The four JSON command components, loot tables, structure loading, and the hasitem selector are all stable and require no experimental toggles. The Script API examples use stable @minecraft/server APIs available in 1.20.80+ and 1.21.x without the Beta APIs toggle.

When it does not work

What you seeWhyThe fix
Syntax error: Unexpected "display": at "{"display"<<:{"Name":"...You attempted to use Java Edition NBT syntax (such as display, Name, Lore, or Enchantments) inside the Bedrock /give command. Bedrock's command parser only accepts the four specific Adventure and inventory components.Move your custom name, lore, and enchantments into a behaviour pack loot table (using set_name, set_lore, and specific_enchants) and give the item using /loot give, or construct the item in JavaScript using @minecraft/server.
Component 'minecraft:can_destroy' could not be updated.One or more block identifiers inside the "blocks" array does not exist, is misspelled, or uses an outdated pre-flattening block name (for example, using "wool" instead of "white_wool" on 1.20+).Check every block identifier in the "blocks" array against current Bedrock block IDs. Ensure you are using the flattened block names introduced in 1.20.x.
The /loot give command runs without an error, but no item appears in the player's inventory.Either the file path passed to /loot give does not match the path inside BP/loot_tables/, or the JSON inside the loot table has a syntax error or invalid item identifier. Look in the Content Log for '[LootTable] ... failed to parse'.Ensure your command uses the path relative to the loot_tables folder without the .json extension (e.g., "items/dungeon_key" for BP/loot_tables/items/dungeon_key.json), and validate your JSON formatting.
Script error: Failed to set dynamic property on item stack: Item is stackable.You called setDynamicProperty() on an ItemStack whose maximum stack size is greater than 1. Bedrock only permits dynamic properties on unstackable items.Only use setDynamicProperty() on vanilla tools/weapons/armor that stack to 1, or on custom items where "minecraft:max_stack_size" is explicitly set to 1 in the item's JSON definition.

Reference, straight from the game files

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