Skip to main content

Overview

Items in Minestom are represented by the ItemStack class, which follows an immutable design pattern. This means once an ItemStack is created, it cannot be modified directly - instead, you create new ItemStack instances with the desired changes.
Never use null for items! Always use ItemStack.AIR instead of null when representing the absence of an item.

Creating Items

Basic Creation

The simplest way to create an item is using the static factory methods:
// Create a single item
ItemStack stone = ItemStack.of(Material.STONE);

// Create a stack with a specific amount
ItemStack diamonds = ItemStack.of(Material.DIAMOND, 64);

Using the Builder Pattern

For more complex items, use the builder pattern:
ItemStack customItem = ItemStack.builder(Material.DIAMOND_SWORD)
    .amount(1)
    .customName(Component.text("Legendary Sword"))
    .lore(
        Component.text("A powerful weapon"),
        Component.text("Forged in the depths")
    )
    .build();

Material Types

Materials represent the type of item. Minestom provides all vanilla Minecraft materials through the Material interface.

Common Materials

Material.STONE
Material.DIAMOND
Material.IRON_SWORD
Material.GOLDEN_APPLE
Material.ENCHANTED_GOLDEN_APPLE

Material Properties

You can check various properties of materials:
Material material = Material.STONE;

// Check if the material is a block
boolean isBlock = material.isBlock();

// Get the block form (if it exists)
Block block = material.block();

// Check if it's armor
boolean isArmor = material.isArmor();

// Get max stack size
int maxStack = material.maxStackSize(); // Default: 64

Getting Materials by Key

// From string key
Material stone = Material.fromKey("minecraft:stone");

// From Key object
Material diamond = Material.fromKey(Key.key("minecraft:diamond"));

// From numeric ID
Material item = Material.fromId(1);

Understanding Immutability

Since ItemStack is immutable, all modification methods return a new ItemStack:
ItemStack original = ItemStack.of(Material.STONE, 1);

// This creates a NEW ItemStack - original is unchanged
ItemStack modified = original.withAmount(64);

// original is still 1, modified is 64
System.out.println(original.amount()); // 1
System.out.println(modified.amount()); // 64
Always use the returned ItemStack from modification methods. The original ItemStack is never changed.

Data Components System

Minestom uses a data component system (matching vanilla Minecraft 1.20.5+) for item properties. Components are accessed through the DataComponents class.

Common Components

// Custom name
DataComponents.CUSTOM_NAME

// Lore (list of components)
DataComponents.LORE

// Enchantments
DataComponents.ENCHANTMENTS

// Max stack size
DataComponents.MAX_STACK_SIZE

// Damage (durability)
DataComponents.DAMAGE

// Max damage (max durability)
DataComponents.MAX_DAMAGE

// Unbreakable flag
DataComponents.UNBREAKABLE

// Custom model data
DataComponents.CUSTOM_MODEL_DATA

// Enchantment glint override
DataComponents.ENCHANTMENT_GLINT_OVERRIDE

// Food properties
DataComponents.FOOD

// Consumable properties
DataComponents.CONSUMABLE

Setting Components

1

Using with() method

Set a component value using the with() method:
ItemStack item = ItemStack.of(Material.DIAMOND_SWORD)
    .with(DataComponents.CUSTOM_NAME, Component.text("Excalibur"))
    .with(DataComponents.LORE, List.of(
        Component.text("A legendary blade"),
        Component.text("Said to grant immense power")
    ));
2

Using builder pattern

Or use the builder’s set() method:
ItemStack item = ItemStack.builder(Material.DIAMOND_SWORD)
    .set(DataComponents.CUSTOM_NAME, Component.text("Excalibur"))
    .set(DataComponents.LORE, List.of(
        Component.text("A legendary blade")
    ))
    .build();
3

Using convenience methods

Many components have convenience methods:
ItemStack item = ItemStack.of(Material.DIAMOND_SWORD)
    .withCustomName(Component.text("Excalibur"))
    .withLore(
        Component.text("A legendary blade"),
        Component.text("Said to grant immense power")
    );

Getting Component Values

ItemStack item = /* ... */;

// Get a component value (returns null if not present)
Component name = item.get(DataComponents.CUSTOM_NAME);

// Get with a default value
int maxStack = item.get(DataComponents.MAX_STACK_SIZE, 64);

// Check if a component exists
boolean hasName = item.has(DataComponents.CUSTOM_NAME);

Removing Components

// Explicitly remove a component
ItemStack item = itemWithName.without(DataComponents.CUSTOM_NAME);
Removing a component explicitly removes it from the item, rather than reverting to the default. For example, removing FOOD from an apple makes it non-edible.

Common Item Modifications

Custom Names and Lore

ItemStack namedItem = ItemStack.builder(Material.DIAMOND)
    .customName(Component.text("Rare Diamond")
        .color(NamedTextColor.AQUA))
    .lore(
        Component.text("Found in the depths"),
        Component.text("Very valuable")
    )
    .build();

Enchantments

Enchantments are stored in an EnchantmentList:
import net.minestom.server.item.component.EnchantmentList;
import net.minestom.server.item.enchant.Enchantment;

ItemStack enchantedSword = ItemStack.builder(Material.DIAMOND_SWORD)
    .set(DataComponents.ENCHANTMENTS, 
        new EnchantmentList(Enchantment.SHARPNESS, 5))
    .build();

// Add multiple enchantments
EnchantmentList enchants = EnchantmentList.EMPTY
    .with(Enchantment.SHARPNESS, 5)
    .with(Enchantment.FIRE_ASPECT, 2)
    .with(Enchantment.UNBREAKING, 3);

ItemStack sword = ItemStack.of(Material.DIAMOND_SWORD)
    .with(DataComponents.ENCHANTMENTS, enchants);

Durability and Damage

ItemStack tool = ItemStack.builder(Material.DIAMOND_PICKAXE)
    .set(DataComponents.MAX_DAMAGE, 1561)  // Max durability
    .set(DataComponents.DAMAGE, 100)        // Current damage
    .build();

// Damage an item
ItemStack damaged = tool.damage(50); // Adds 50 damage

// Make item unbreakable
ItemStack unbreakable = tool.with(DataComponents.UNBREAKABLE);

Custom Model Data

import net.minestom.server.item.component.CustomModelData;
import net.kyori.adventure.util.RGBLike;

ItemStack customModel = ItemStack.builder(Material.STICK)
    .customModelData(
        List.of(1.0f, 2.0f),           // floats
        List.of(true, false),          // flags
        List.of("custom", "data"),     // strings
        List.of(Color.rgb(255, 0, 0))  // colors
    )
    .build();

Glowing Effect

// Make an item glow (enchantment glint)
ItemStack glowing = ItemStack.of(Material.STICK)
    .withGlowing(true);

// Or using builder
ItemStack glowingItem = ItemStack.builder(Material.STICK)
    .glowing()
    .build();

Max Stack Size

// Change max stack size
ItemStack singleStack = ItemStack.of(Material.ENDER_PEARL)
    .withMaxStackSize(1);

// Or using builder
ItemStack item = ItemStack.builder(Material.DIAMOND)
    .maxStackSize(64)
    .build();

Unbreakable Items

ItemStack unbreakable = ItemStack.of(Material.DIAMOND_SWORD)
    .with(DataComponents.UNBREAKABLE);

Item Meta

Food Items

import net.minestom.server.item.component.Food;

ItemStack customFood = ItemStack.builder(Material.APPLE)
    .set(DataComponents.FOOD, new Food(
        4,       // nutrition
        0.3f,    // saturation modifier
        false    // can always eat
    ))
    .build();

Consumable Items

import net.minestom.server.item.component.Consumable;
import net.minestom.server.item.ItemAnimation;
import net.minestom.server.sound.SoundEvent;

ItemStack consumable = ItemStack.builder(Material.IRON_NUGGET)
    .set(DataComponents.CONSUMABLE, new Consumable(
        1.0f,                          // consume time (seconds)
        ItemAnimation.EAT,             // animation
        SoundEvent.BLOCK_CHAIN_STEP,   // sound
        true,                          // has consume particles
        new ArrayList<>()              // consume effects
    ))
    .build();

Armor Trim

import net.minestom.server.item.component.ArmorTrim;
import net.minestom.server.item.armor.TrimMaterial;
import net.minestom.server.item.armor.TrimPattern;
import net.minestom.server.registry.Holder;

ItemStack trimmedArmor = ItemStack.builder(Material.DIAMOND_CHESTPLATE)
    .set(DataComponents.TRIM, new ArmorTrim(
        Holder.direct(TrimMaterial.COPPER),
        Holder.direct(TrimPattern.COAST)
    ))
    .build();

Charged Projectiles (Crossbow)

ItemStack loadedCrossbow = ItemStack.of(Material.CROSSBOW)
    .with(DataComponents.CHARGED_PROJECTILES, List.of(
        ItemStack.of(Material.ARROW)
    ));

Bundle Contents

ItemStack bundle = ItemStack.builder(Material.BUNDLE)
    .set(DataComponents.BUNDLE_CONTENTS, List.of(
        ItemStack.of(Material.DIAMOND, 5),
        ItemStack.of(Material.RABBIT_FOOT, 5)
    ))
    .build();

Block Predicates (Adventure Mode)

import net.minestom.server.item.component.BlockPredicates;
import net.minestom.server.instance.block.predicate.BlockPredicate;
import net.minestom.server.instance.block.Block;

ItemStack adventureTool = ItemStack.builder(Material.DIAMOND_PICKAXE)
    .set(DataComponents.CAN_BREAK, new BlockPredicates(
        new BlockPredicate(Block.DIAMOND_ORE)
    ))
    .set(DataComponents.CAN_PLACE_ON, new BlockPredicates(
        new BlockPredicate(Block.STONE)
    ))
    .build();

Giving Items to Players

Adding to Inventory

Player player = /* ... */;

// Add a single item
ItemStack item = ItemStack.of(Material.DIAMOND, 10);
player.getInventory().addItemStack(item);

// Add multiple items
List<ItemStack> items = List.of(
    ItemStack.of(Material.DIAMOND, 64),
    ItemStack.of(Material.GOLD_INGOT, 32),
    ItemStack.of(Material.IRON_INGOT, 16)
);
player.getInventory().addItemStacks(items);

Setting Specific Slots

PlayerInventory inventory = player.getInventory();

// Set main hand (slot 0-8)
inventory.setItemStack(0, ItemStack.of(Material.DIAMOND_SWORD));

// Set armor slots
inventory.setHelmet(ItemStack.of(Material.DIAMOND_HELMET));
inventory.setChestplate(ItemStack.of(Material.DIAMOND_CHESTPLATE));
inventory.setLeggings(ItemStack.of(Material.DIAMOND_LEGGINGS));
inventory.setBoots(ItemStack.of(Material.DIAMOND_BOOTS));

// Set offhand
inventory.setItemInOffHand(ItemStack.of(Material.SHIELD));

Using Transactions

import net.minestom.server.inventory.TransactionOption;

// Add items with transaction options
player.getInventory().addItemStacks(items, TransactionOption.ALL);

Item Events

Item Drop Event

Triggered when a player drops an item:
globalEventHandler.addListener(ItemDropEvent.class, event -> {
    Player player = event.getPlayer();
    ItemStack droppedItem = event.getItemStack();
    
    // Cancel the drop
    // event.setCancelled(true);
    
    // Spawn item entity in the world
    Pos playerPos = player.getPosition();
    ItemEntity itemEntity = new ItemEntity(droppedItem);
    itemEntity.setPickupDelay(Duration.of(500, TimeUnit.MILLISECOND));
    itemEntity.setInstance(player.getInstance(), playerPos.withY(y -> y + 1.5));
    
    Vec velocity = playerPos.direction().mul(6);
    itemEntity.setVelocity(velocity);
});

Item Pickup Event

Triggered when a living entity picks up an item:
globalEventHandler.addListener(PickupItemEvent.class, event -> {
    Entity entity = event.getLivingEntity();
    ItemStack itemStack = event.getItemStack();
    
    if (entity instanceof Player player) {
        // Cancel if player doesn't have space
        boolean hasSpace = player.getInventory().addItemStack(itemStack);
        event.setCancelled(!hasSpace);
    }
});

Item Use Events

globalEventHandler.addListener(PlayerBeginItemUseEvent.class, event -> {
    Player player = event.getPlayer();
    ItemStack itemStack = event.getItemStack();
    PlayerHand hand = event.getHand();
    
    // Example: Check for loaded crossbow
    if (itemStack.material() == Material.CROSSBOW) {
        List<ItemStack> projectiles = itemStack.get(
            DataComponents.CHARGED_PROJECTILES, 
            List.of()
        );
        
        if (!projectiles.isEmpty()) {
            // Shoot the arrow
            player.setItemInHand(hand, 
                itemStack.without(DataComponents.CHARGED_PROJECTILES));
            player.sendMessage("pew pew!");
            event.setItemUseDuration(0); // Don't start using
        }
    }
});

Use Item On Block

globalEventHandler.addListener(PlayerUseItemOnBlockEvent.class, event -> {
    if (event.getHand() != PlayerHand.MAIN) return;
    
    Player player = event.getPlayer();
    ItemStack itemStack = event.getItemStack();
    Block block = event.getInstance().getBlock(event.getPosition());
    
    // Example: Waterlog blocks with bucket
    if ("false".equals(block.getProperty("waterlogged")) 
        && itemStack.material().equals(Material.WATER_BUCKET)) {
        block = block.withProperty("waterlogged", "true");
        event.getInstance().setBlock(event.getPosition(), block);
    }
});

Creative Inventory Action

globalEventHandler.addListener(CreativeInventoryActionEvent.class, event -> {
    ItemStack clickedItem = event.getClickedItem();
    
    // Example: Replace apples with golden apples
    if (clickedItem.material() == Material.APPLE) {
        event.setClickedItem(
            ItemStack.of(Material.GOLDEN_APPLE, clickedItem.amount())
        );
    }
    
    // Example: Prevent enchanted golden apples
    if (clickedItem.material() == Material.ENCHANTED_GOLDEN_APPLE) {
        event.setCancelled(true);
    }
});

Complete Examples

Custom Weapon with Enchantments

public ItemStack createLegendarySword() {
    EnchantmentList enchants = EnchantmentList.EMPTY
        .with(Enchantment.SHARPNESS, 10)
        .with(Enchantment.FIRE_ASPECT, 5)
        .with(Enchantment.UNBREAKING, 10)
        .with(Enchantment.LOOTING, 5);
    
    return ItemStack.builder(Material.DIAMOND_SWORD)
        .customName(Component.text("Excalibur")
            .color(NamedTextColor.GOLD)
            .decorate(TextDecoration.BOLD))
        .lore(
            Component.text("A legendary blade from ancient times"),
            Component.empty(),
            Component.text("Special Abilities:")
                .color(NamedTextColor.GRAY),
            Component.text("• Massive damage")
                .color(NamedTextColor.GREEN),
            Component.text("• Sets enemies ablaze")
                .color(NamedTextColor.RED),
            Component.text("• Never breaks")
                .color(NamedTextColor.AQUA)
        )
        .set(DataComponents.ENCHANTMENTS, enchants)
        .glowing()
        .build();
}

Custom Food Item

public ItemStack createCustomFood() {
    return ItemStack.builder(Material.COOKED_BEEF)
        .customName(Component.text("Dragon Steak")
            .color(NamedTextColor.RED))
        .lore(Component.text("Restores health and grants strength"))
        .set(DataComponents.FOOD, new Food(
            20,      // nutrition (10 hunger bars)
            2.0f,    // saturation modifier
            true     // can always eat
        ))
        .set(DataComponents.CONSUMABLE, new Consumable(
            0.8f,                        // fast eating
            ItemAnimation.EAT,
            SoundEvent.ENTITY_PLAYER_BURP,
            true,
            new ArrayList<>()
        ))
        .build();
}

Starter Kit

public void giveStarterKit(Player player) {
    List<ItemStack> items = List.of(
        // Tools
        ItemStack.of(Material.WOODEN_PICKAXE),
        ItemStack.of(Material.WOODEN_AXE),
        ItemStack.of(Material.WOODEN_SHOVEL),
        
        // Armor
        ItemStack.of(Material.LEATHER_HELMET),
        ItemStack.of(Material.LEATHER_CHESTPLATE),
        ItemStack.of(Material.LEATHER_LEGGINGS),
        ItemStack.of(Material.LEATHER_BOOTS),
        
        // Food
        ItemStack.of(Material.BREAD, 16),
        ItemStack.of(Material.COOKED_BEEF, 8),
        
        // Resources
        ItemStack.of(Material.OAK_LOG, 32),
        ItemStack.of(Material.COBBLESTONE, 64),
        ItemStack.of(Material.TORCH, 16)
    );
    
    player.getInventory().addItemStacks(items, TransactionOption.ALL);
    
    // Set armor directly
    player.getInventory().setHelmet(ItemStack.of(Material.LEATHER_HELMET));
    player.getInventory().setChestplate(ItemStack.of(Material.LEATHER_CHESTPLATE));
    player.getInventory().setLeggings(ItemStack.of(Material.LEATHER_LEGGINGS));
    player.getInventory().setBoots(ItemStack.of(Material.LEATHER_BOOTS));
}

Efficiency Tool for Instabreak

public ItemStack createInstabreakTool() {
    EnchantmentList efficiency = EnchantmentList.EMPTY
        .with(Enchantment.EFFICIENCY, 5);
    
    return ItemStack.builder(Material.DIAMOND_PICKAXE)
        .customName(Component.text("Instabreak Pickaxe"))
        .set(DataComponents.ENCHANTMENTS, efficiency)
        .with(DataComponents.UNBREAKABLE)
        .build();
}

Best Practices

Always Use ItemStack.AIR

Never use null to represent no item. Always use ItemStack.AIR instead.

Remember Immutability

ItemStack methods return new instances. Always use the returned value.

Use Components

Use the DataComponents system for all item properties instead of deprecated meta systems.

Validate Materials

Check if a material exists before using it, especially when loading from config.
Use the builder pattern for complex items with multiple properties - it’s more readable and less error-prone than chaining many with() calls.

Advanced Topics

Custom Tags

You can store custom data on items using tags:
import net.minestom.server.tag.Tag;

Tag<String> customTag = Tag.String("my_custom_data");

ItemStack item = ItemStack.of(Material.STICK)
    .withTag(customTag, "my value");

// Read the tag
String value = item.getTag(customTag);

Item Similarity

Check if two items are similar (same material and components, ignoring amount):
ItemStack item1 = ItemStack.of(Material.DIAMOND, 1);
ItemStack item2 = ItemStack.of(Material.DIAMOND, 64);

boolean similar = item1.isSimilar(item2); // true

Converting to NBT

import net.kyori.adventure.nbt.CompoundBinaryTag;

ItemStack item = ItemStack.of(Material.DIAMOND);

// Convert to NBT
CompoundBinaryTag nbt = item.toItemNBT();

// Load from NBT
ItemStack fromNbt = ItemStack.fromItemNBT(nbt);

Item Consumption

// Consume (decrease) amount
ItemStack stack = ItemStack.of(Material.DIAMOND, 10);
ItemStack consumed = stack.consume(3); // Amount is now 7

Common Patterns

Modifying Items in Inventory

PlayerInventory inventory = player.getInventory();
int slot = 0;

// Get current item
ItemStack current = inventory.getItemStack(slot);

// Modify it (creates new instance)
ItemStack modified = current.withAmount(current.amount() - 1);

// Set it back
inventory.setItemStack(slot, modified);

Checking Item Properties

ItemStack item = player.getItemInMainHand();

// Check if air
if (item.isAir()) {
    player.sendMessage("You're not holding anything!");
    return;
}

// Check material
if (item.material() == Material.DIAMOND_SWORD) {
    player.sendMessage("Nice sword!");
}

// Check amount
if (item.amount() > 1) {
    player.sendMessage("You have " + item.amount() + " items");
}

Build docs developers (and LLMs) love