Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Items and stacks

Verified against Minecraft 26.3 · Part VII · A diamond pickaxe sits in a hotbar slot, is compared against its neighbours, is sent to a client, and finally loses its last point of durability.

A diamond pickaxe is in your hotbar. The Item behind it, Items.DIAMOND_PICKAXE, is a single object shared by every diamond pickaxe that has ever existed on this server, and it holds four fields: a description id, a crafting remainder, a feature-flag set, and its own registry holder — Item.descriptionId, Item.craftingRemainingItem, a FeatureFlagSet in Item.requiredFeatures, and Item.builtInRegistryHolder. Not the stack size. Not the mining speed. Not the durability. All of that is data components — and they do not live on the Item either. They live on the item’s Holder.Reference in BuiltInRegistries.ITEM, as a prototype map built at the first data-pack load and rebuilt at every one after (data components), and the stack borrows that map read-only and stores only the ways it differs from it. This page is the object in the slot rather than the component system behind it: what an ItemStack is made of, what makes two of them the same stack, what a stack may legally hold, and what happens to one that runs out of durability. That last is the odd one out in a part where almost everything is predicted locally and corrected afterwards. A client never even guesses at the durability an item spends — the method that spends it demands a ServerLevel outright, and the convenient overloads that take a LivingEntity instead look its level up and silently do nothing when the answer is a client’s.

The cast

classwhat it decidesthread
Itemthe behaviour hooks, and four fields that are not componentsboth
Item.Propertiesthe builder — which produces an initializer, never a component mapclass-init, wherever the bootstrap runs
Holder.Referencewhere an item’s default components live, and whether they exist yetwritten by the thread loading the world, on the Server thread at each /reload, and on the Render thread for a joining client
DataComponentInitializersthe pile of pending default maps, one entry per registered itembuilt on the worker pool on the server, on the Render thread on a joining client
ItemStacka holder, a count, a pop time and a patched map — the mutable thing in a slotboth
PatchedDataComponentMapprototype plus patch, and the copy-on-write flag that makes copying a stack cheapwherever its stack is
ItemStackTemplatethe immutable stack: what a stack looks like inside a component, a particle or a recipeboth
ItemEntitya stack that is an entity, with a five-minute clock and a merge ruleServer, mirrored on the Render thread

Four fields, and only one of them is really data

ItemStack is a final class holding exactly four things: two ints, a registry holder, and the interesting one.

classDiagram
    class ItemStack {
        int count
        int popTime
        Holder~Item~ item
        PatchedDataComponentMap components
    }
    class Item {
        String descriptionId
        ItemStackTemplate craftingRemainingItem
        FeatureFlagSet requiredFeatures
        Holder.Reference builtInRegistryHolder
    }
    class Reference["Holder.Reference, in BuiltInRegistries.ITEM"] {
        the Item
        the item's default DataComponentMap
    }
    class PatchedDataComponentMap {
        the prototype, borrowed
        the patch, owned
    }
    ItemStack *-- PatchedDataComponentMap : the only mutable half
    ItemStack --> Reference : every stack of this item, the one holder
    Reference --> Item
    PatchedDataComponentMap ..> Reference : reads the defaults, never writes them

ItemStack’s four fields and Item’s four, and the arrows between the classes. The dotted one is the shape of the whole system — a stack reads defaults it can never touch.

A stack does not own its defaults and cannot change them: it points at a holder, and the holder owns one DataComponentMap shared by every stack of that item in both programs (a reload rebinds it, and a stack made before keeps the map it was made with). What the patch is made of, and why copying a stack is cheap, belong to data components.

The holder field the figure calls item is read through ItemStack.typeHolder, and it answers Items.AIR’s holder rather than null for an empty stack, which is why ItemStack.getItem never returns null either. The pop time — ItemStack.popTime — is the odd one out: it is the five-tick squeeze the hotbar icon does when something lands in it, set to 5 by Inventory when a stack grows or a damaged item lands in an empty slot, and by ClientPacketListener.handleContainerSetSlot when a slot update makes a hotbar stack larger, counted down by ItemStack.inventoryTick on both sides, and read by Hud.extractSlot, which scales the icon while it is above zero and hands the drawing to GuiGraphicsExtractor either way. It is ordinary shared state that only the client has any use for — and ItemStack.copy carries it across, so the animation survives being copied into a menu slot.

Item.Properties is the builder used at class-init, and its output is not a component map. Item.Properties.component and every convenience over it — Item.Properties.stacksTo, Item.Properties.durability, Item.Properties.food, Item.Properties.tool, Item.Properties.spear, Item.Properties.equippable, Item.Properties.useCooldown — fold one more step onto a DataComponentInitializers.Initializer, a function that will be run against a DataComponentMap.Builder later, with a HolderLookup.Provider in hand. The ones that make a weapon are a different seven — Item.Properties.tool, Item.Properties.spear and five named for what they make — and between them they build forty-two items out of a registry of over a thousand (the weapon helpers).

Between the two halves of that arrangement an Item is a live object with no components at all: the constructor registers an initializer and the map is not built until a reload builds it, so Item.components throws until then and Item.CODEC_WITH_BOUND_COMPONENTS exists to refuse an item whose components are not bound yet. When and on which thread that happens, on the server and on a joining client alike, is data components.

What copying a stack costs, and what crosses the wire

Two properties of that borrowed map do work on the rest of this page, and both belong to data components. The patch is sanitised on every write, so it can never hold a value the item already had — which is what makes the equality table below behave. And the patch map is shared until someone writes to it: ItemStack.copy hands out the same map and sets a copy-on-write flag, and the fork happens on the first mutation. Copying a stack is therefore something the game does without thinking about it, and it does — menus, recipes and ServerPlayerGameMode.destroyBlock all copy constantly, and each copy allocates two small objects and no component data.

The wire form falls out of the same shape and is the second of the four serialisations codecs, NBT and JSON lays side by side: ItemStack.OPTIONAL_STREAM_CODEC writes the count, the item holder and the patch, never the prototype, because the receiver has the same prototype bound to the same holder. A count of zero is the whole encoding of an empty stack.

When two stacks are the same stack

Five static methods on ItemStack answer five different versions of that question, and menus, recipes and the first-person hand each want a different one.

methodcomparesused for
ItemStack.isSameItemthe item holder onlyis this the same kind of thing
ItemStack.isSameItemSameComponentsthe item, then the whole PatchedDataComponentMapstacking, and every are these interchangeable test
ItemStack.matchesthe count as wellcontainer synchronisation (containers and menus)
ItemStack.matchesIgnoringComponentseverything except the component types a predicate excusesthe held-item swap animation
ItemStack.hashItemAndComponentsthe item’s hash and the effective component map’skeying stacks in hash sets

The second row is where the borrowed map pays off. Comparing two stacks of the same item amounts to comparing their patches, and because a patch never holds a value equal to the prototype’s, two pickaxes with the same damage are equal whether one reached that state by being set explicitly or by never being touched.

The fourth row exists for one component. DataComponents.DAMAGE is the only component type in the game declared with DataComponentType.Builder.ignoreSwapAnimation, and FirstPersonHandsAndItems.shouldInstantlyReplaceVisibleItem passes exactly that flag as the predicate — which is why a pickaxe losing a point of durability does not re-play the lower-and-raise animation. And a trap sits under all five rows: ItemStack.EMPTY is a singleton but is not identified by reference, because ItemStack.isEmpty also answers true for Items.AIR and for any count at or below zero.

Two validators, one rule, two spellings

Durability and stackability are mutually exclusive, and the game says so twice — in different places, at different times, against different components.

the reload validatorItemStack.validateStrict
installed byItem.Properties.finalizeInitializer—
rejectsDataComponents.DAMAGE on a stackable itemDataComponents.MAX_DAMAGE on a stackable item, and a count over the maximum
runs insideDataComponentMap.Builder.buildItemInput, ItemStack.applyComponentsAndValidate, and ItemStackTemplate.create and ItemStackTemplate.apply through one private step they share
whenat reload, on the worker pool (a joining client’s on the Render thread)when a command, a template or a component patch builds a stack
on failurethrows, failing the reloaddepends on the caller: ItemInput throws a command syntax error, the other two log and yield ItemStack.EMPTY or restore the previous patch

Neither is reached from a network decode. Exactly one serverbound packet in the protocol carries an ItemStack at all, the creative slot’s, and it is proved a third way — re-encoded rather than validated (codecs, NBT and JSON).

The strict validator reaches one level into a stack’s contents and no further. ItemStack.validateContainedItemSizes runs inside ItemStack.validateStrict over DataComponents.CONTAINER, DataComponents.BUNDLE_CONTENTS and DataComponents.CHARGED_PROJECTILES, checking each contained stack’s count against its own maximum — and it does not re-run the full validation there, so nesting is not followed; a shulker box full of impossible stacks is caught by a command, and not at the creative slot’s door, which never runs the strict validator at all. The bundle is the one that gets a second test of its own: a BundleContents whose weight cannot be computed fails too, though one merely over its capacity passes.

An item, a count, some components — said three ways

ItemInstance is the read-only contract the contents check is written against: ItemInstance.count, ItemInstance.getMaxStackSize, and — through TypedInstance and DataComponentGetter — five TypedInstance.is overloads for tags, holder sets, raw items, holders and resource keys, to which ItemStack.is adds a sixth taking a predicate. Its default ItemInstance.getMaxStackSize answers 1 when DataComponents.MAX_STACK_SIZE is missing — which for a bound item never happens, because the common set puts 64 there.

Two classes implement it. ItemStack is the mutable one that lives in slots. ItemStackTemplate is an immutable record of a Holder<Item>, a count and a raw DataComponentPatch — one the template itself never sanitises against a prototype, so a template read from data can carry a component value equal to the item’s own default, which no ItemStack can. It is what a stack becomes when it is stored inside something else: ItemContainerContents (so a shulker box’s contents are templates, not stacks), BundleContents, ChargedProjectiles, ItemParticleOption, HoverEvent, UseRemainder, the recipe classes, and Item’s own crafting remainder. Its constructor refuses a count of zero or Items.AIR, so there is no empty template; but ItemStackTemplate.create and ItemStackTemplate.apply, which materialise a real stack, run ItemStack.validateStrict on the result and answer ItemStack.EMPTY with a log line rather than throwing.

A pickaxe’s last point of durability

Durability is not one component: ItemStack.isDamageableItem demands DataComponents.MAX_DAMAGE present, DataComponents.UNBREAKABLE absent, and DataComponents.DAMAGE present. Take a diamond pickaxe one block short of breaking, and mine that block.

ServerPlayerGameMode.destroyBlock copies the held stack before touching it — that copy is what Block.playerDestroy later hands the loot table, so the drops are decided by the tool as it was — then calls ItemStack.mineBlock, which delegates to Item.mineBlock and awards Stats.ITEM_USED if the item claims the block. The base Item.mineBlock is pure component work: it reads DataComponents.TOOL, does nothing without one, and damages the stack by Tool.damagePerBlock only on a server level, only when that number is above zero, and only when the block’s destroy speed is not zero — so instant-break plants cost a tool nothing, and neither does a tool that declares no per-block damage.

ItemStack.hurtAndBreak is the way in for almost everything, and the overload that does the work demands a ServerLevel outright. The overloads taking a LivingEntity pattern-match on the entity’s level and silently do nothing on the client, which is why a client never predicts the durability an item spends. The amount then goes through EnchantmentHelper.processDurabilityChange (enchantments), which is how Unbreaking turns a point of damage into no damage at all, and a player with Player.hasInfiniteMaterials short-circuits to zero before even that. If anything survives, the stack fires CriteriaTriggers.ITEM_DURABILITY_CHANGED for a real player, writes the new DataComponents.DAMAGE, and — this being the last point — finds ItemStack.isBroken true, shrinks itself by one, and calls the break hook it was handed. For equipment that hook is LivingEntity.onEquippedItemBroken, which strips the item’s attribute modifiers and broadcasts an entity event (47 for the main hand); ServerPlayer.onEquippedItemBroken adds Stats.ITEM_BROKEN on top.

What the client is told, and what it draws

The client is told in one byte. LivingEntity.handleEntityEvent turns event 47 into LivingEntity.breakItem, which plays DataComponents.BREAK_SOUND from the stack still in that slot and spawns five item particles; the empty slot itself arrives separately, as a container update. Two siblings round the family out: ItemStack.hurtWithoutBreaking clamps one short of the maximum, and ItemStack.hurtAndConvertOnBreak transmutes rather than vanishing.

What the player watched was three methods on Item. Item.isBarVisible is is this stack damaged, Item.getBarWidth scales the damage over the thirteen pixels Item.MAX_BAR_WIDTH names, and Item.getBarColor sweeps a hue from green to red. GuiGraphicsExtractor draws the two-pixel bar under the icon from those three answers and nothing else.

All three read the client’s own copy of the stack, and the client never wrote to it. So the bar does not creep as you mine: it sits at whatever the last slot update said, and jumps when the next one arrives. The one point of durability you just spent is invisible until the server sends the slot back.

The tick a stack gets, and the stack that is an entity

ItemStack.inventoryTick runs on both sides and does exactly one thing there: decrement the pop time. It forwards to Item.inventoryTick only for a ServerLevel — the hook’s parameter is declared as one, so it cannot be otherwise — and exactly two items override that hook, CompassItem and MapItem. It has two callers: Inventory.tick, from Player.aiStep, walks the thirty-six ordinary slots and tells the selected one it is the main hand; EntityEquipment.tick, from LivingEntity.aiStep, walks the worn and held slots of every living entity. A stack that leaves an inventory altogether becomes an ItemEntity, which keeps it in a synched data entry (synched entity data) rather than a plain field, counts up to a 6000-tick lifetime, and folds itself into neighbours through ItemEntity.mergeWithNeighbours — a merge that keeps the smaller of the two ages, so a fresh drop rejuvenates an old one. Blocks reach it through Block.popResource (block breaking).

The seventy-nine classes, and why sixty-seven of them are almost empty

world/item’s own directory holds seventy-nine classes beside its records, enums and interfaces. Two of them are Item and ItemStack and ten are helpers such as Items and ItemCooldowns; the other sixty-seven are one Item subclass each, and all but two exist for the same reason — a behaviour hook that no component can express (BannerItem adds only an accessor, and ProjectileWeaponItem is the bow’s and the crossbow’s shared base). BoneMealItem, HoneycombItem, EnderEyeItem, DebugStickItem, LeadItem and thirty-four more override Item.use, Item.useOn or Item.interactLivingEntity and hold no state of their own. Axes, shovels and hoes are plain Items: stripping, path-making and tilling are one component, DataComponents.BLOCK_TRANSFORMER, that the base Item.useOn reads and runs. That is why a registry of over a thousand items has so few classes behind it (the class hierarchy).

What this page hands off

Everything a stack does when a player holds down the use key — the prediction, the countdown, the consumable and cooldown components, the completion packet — is the next lecture: using an item. How two machines agree about a set of stacks in a screen is containers and menus; the component system itself is data components, catalogued in the components reference. And how an item picks the model, texture and tint you see in the slot is not this part’s subject at all: it is Part XI’s, in models and atlases.

For a 1.21-era reader. AxeItem, ShovelItem and HoeItem are gone: DataComponents.BLOCK_TRANSFORMER does their work, naming a BlockTransformer from a data registry. ItemInHandRenderer is gone too; FirstPersonHandsAndItems decides the swap animation.

Where to look

Open ItemStack first and read its four fields; everything else on this page is a consequence of them. PatchedDataComponentMap beside it is where the copy-on-write flag and the sanitising live, and it is the shortest way to see why two pickaxes with the same damage are equal.

Then Item and Item.Properties, in that order, for the gap between a live item and its components: the builder’s output is a DataComponentInitializers.Initializer, and where it eventually lands is Holder.Reference in BuiltInRegistries.ITEM. Items is the roll call of what was built.

For the durability trace, ServerPlayerGameMode.destroyBlock is the entry point and ItemStack.hurtAndBreak is where it ends up. For the immutable twin, ItemStackTemplate; for the contract both stacks answer, ItemInstance. ItemEntity is the stack that left the inventory, and Inventory.tick and EntityEquipment.tick are the two walks that tick the stacks that did not.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.