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

Data components

Verified against Minecraft 26.3 · Part II · A player types /give @s diamond_sword[enchantments={sharpness:3}], and later rolls Sharpness onto a plain sword at the enchanting table: what the square brackets are, and how the client finds out.

A player types /give @s diamond_sword[enchantments={sharpness:3}]. The part in square brackets is a patch: one keyed, typed value laid over the sword. A data component is exactly that — one typed, keyed piece of data attached to an item stack: its damage, its enchantments, its lore, the food it is, the armour slot it goes in. The item type supplies a prototype map of components; a stack carries only a patch against that prototype. Everything that used to be “the NBT on an item” is a component with a codec, and most of what used to be a subclass of Item carrying behaviour (a sword, a piece of armour) is now a kit of components on a plain Item. The surprise is where the prototype comes from. It is not built in the item’s constructor: Item.Properties only records an initializer, and the map is built again on every reload with the world’s registries in hand — DataComponentInitializers.build on the reload worker, Holder.Reference.bindComponents on the owning thread. That is why a data pack can change what a jukebox plays without touching the item, and why a stack cannot even be decoded before the first reload: Item.CODEC_WITH_BOUND_COMPONENTS guards on Holder.areComponentsBound.

The cast

classwhat it decidesthread
DataComponentTypethe key: which codec writes the value to disk, which to the wire, and whether it is saved at allstatic, registered at bootstrap
DataComponentMapthe map interface; a prototype is an immutable, identity-keyed oneany; a prototype is never mutated
PatchedDataComponentMapwhat an ItemStack owns: a shared prototype, a patch, and whether the patch is still someone else’swhichever thread owns the stack
DataComponentPatchthe serialisable form of the patch: additions and removalsNetty for packets; server and workers for saves
DataComponentInitializersbuilds every registry element’s prototype, from the recorded initializers, once the registries existthe reload worker on the server; the Render thread on the client
Holder.Referenceholds the bound prototype for one registry element, and throws until it has onebound by the thread driving a world load, and by the server thread on /reload; on the client at the end of configuration
ItemStackthe writes (ItemStack.set, ItemStack.update, ItemStack.remove, ItemStack.copyFrom) and the reads it inherits from DataComponentHolderwhichever thread owns the stack
DataComponentLookupthe reverse index: which elements of a registry carry this component valuebuilt at freeze, populated lazily

Everything here ships in both jars; in the trace below only ClientPacketListener and RegistryDataCollector are client-only.

The shape of a stack

classDiagram
    class Item {
        Holder.Reference builtInRegistryHolder
    }
    class ItemStack {
        PatchedDataComponentMap components
    }
    class PatchedDataComponentMap {
        DataComponentMap prototype
        Map patch
        boolean copyOnWrite
    }
    class DataComponentPatch {
        added values
        removed keys
    }
    class DataComponentType {
        a persistent codec
        a network codec
        or only the latter
    }
    ItemStack *-- PatchedDataComponentMap
    PatchedDataComponentMap ..> Item : many stacks, one prototype
    PatchedDataComponentMap --> DataComponentPatch : asPatch
    DataComponentPatch --> PatchedDataComponentMap : fromPatch, given a prototype
    DataComponentType <.. Item : keys the prototype
    DataComponentType <.. DataComponentPatch : keys the patch

What a stack holds and what it only points at: a stack owns its PatchedDataComponentMap and that map owns the patch, while the dotted line to Item points at a prototype every diamond sword made between two reloads shares and none can write. DataComponentPatch is the form that leaves, on disk and on the wire, and PatchedDataComponentMap.fromPatch is how it comes back.

The item’s prototype is built by DataComponentInitializers and bound onto the item’s Holder.Reference, which a section below is about. A stack points at that shared prototype and owns only a patch over it, in which a removal is a marker value, Removed.INSTANCE. The patch’s serialisable twin is a DataComponentPatch, and the key of every entry in all of them is a DataComponentType. The rest of the page is a tour of those objects, each grounded in one small trace: Sharpness III arriving on a sword at the enchanting table.

The key: DataComponentType

A type is built by DataComponentType.Builder. DataComponentType.Builder.persistent gives the disk and JSON codec; DataComponentType.Builder.networkSynchronized a hand-written wire codec. A type with no persistent codec is transient (DataComponentType.isTransient): never saved, only sent. DataComponentType.Builder.networkSynchronized is not a gate — without it, DataComponentType.Builder.build derives a wire codec from the persistent codec (NBT over the wire), and with neither it throws. The only real switch is transient-versus-persistent, and it gates saving. Three types exist only on the wire: DataComponents.CREATIVE_SLOT_LOCK, DataComponents.ADDITIONAL_TRADE_COST, DataComponents.MAP_POST_PROCESSING.

122 vanilla types are registered in DataComponents into BuiltInRegistries.DATA_COMPONENT_TYPE; 30 of them have slash-shaped ids (villager/variant and its siblings). The catalogue is reference/components. Component types are code, in a registry data packs cannot extend; what data packs reach is the prototype (below) and CustomData, which carries arbitrary NBT for them to use.

Two more flags live on the builder. DataComponentType.Builder.cacheEncoding routes a type’s Codec encodes — disk and JSON, not the wire — through EncoderCache (DataComponents.ENCODER_CACHE). DataComponentType.Builder.ignoreSwapAnimation is set on exactly one type, DataComponents.DAMAGE, and the first-person hand that reads it belongs to items and stacks. Underneath, DataComponentType.PERSISTENT_CODEC and DataComponentType.VALUE_MAP_CODEC are the shared dispatch machinery that DataComponentMap.CODEC and the exact predicate are built on.

The maps

A prototype DataComponentMap is immutable and identity-keyed; DataComponentMap.Builder builds one and can carry a DataComponentMap.Builder.addValidator, which is where the prototype-time structural rule lives (below). DataComponentMap.EMPTY is the map every registry element gets when nothing declared one. DataComponentMap.composite is dead API: it exists, and nothing in 26.3 calls it; the layering that actually happens is PatchedDataComponentMap’s prototype-plus-patch.

PatchedDataComponentMap is the map an ItemStack owns: a PatchedDataComponentMap.prototype shared with every stack of that item made since the last reload, a PatchedDataComponentMap.patch in which the marker Removed.INSTANCE means removed from the prototype, and a PatchedDataComponentMap.copyOnWrite flag. It sanitises on every write: PatchedDataComponentMap.set stores nothing when the value equals the prototype’s, and PatchedDataComponentMap.remove stores a removal marker only if the prototype had the key. In the trace, the sword’s prototype carries an empty ItemEnchantments (every item’s does, through DataComponents.COMMON_ITEM_COMPONENTS), so Sharpness III differs from the prototype and the patch gains one entry. Set the enchantments back to empty and the entry vanishes rather than becoming an explicit default.

The first write pays for the copy. PatchedDataComponentMap.ensureMapOwnership clones the backing map only when PatchedDataComponentMap.copyOnWrite is set; ItemStack.copy, PatchedDataComponentMap.asPatch and ItemStack.transmuteCopy can all alias the same map and set the flag, so copying a stack is O(1) until someone writes. ItemStack.applyComponentsAndValidate relies on that: it snapshots with PatchedDataComponentMap.asPatch, applies, runs ItemStack.validateStrict, and on failure PatchedDataComponentMap.restorePatch.

Equality is prototype plus patch. ItemStack.isSameItemSameComponents bottoms out in PatchedDataComponentMap.equals. Because setting a value equal to the prototype default removes it from the patch, two stacks that reached the default by different routes compare equal.

The patch, on the wire and on disk

DataComponentPatch is the serialisable form: additions and removals. DataComponentPatch.CODEC writes removals as !minecraft:foo; DataComponentPatch.STREAM_CODEC writes two counts then the entries; DataComponentPatch.DELIMITED_STREAM_CODEC is the length-prefixed variant for untrusted input; and DataComponentPatch.split is the added/removed decomposition that the hashing and block-entity paths are both built on. TypedDataComponent — a type with its value — has its own “type id then value” stream codec, distinct from the patch encoding, for the places one value travels alone.

The wire patch never contains defaults — for a real stack. Because PatchedDataComponentMap sanitises on every write, a value equal to the prototype’s is dropped rather than sent. ItemStackTemplate is the exception, and items and stacks says why.

On the network the patch travels inside every ItemStack in ClientboundContainerSetSlotPacket, ClientboundContainerSetContentPacket, ClientboundSetCursorItemPacket, ClientboundSetPlayerInventoryPacket and ClientboundSetEquipmentPacket; the client answers ordinary clicks with hashes, not stacks, and the creative slot alone sends a full, re-validated stack — codecs, NBT and JSON owns the four serialisations of a stack, and containers and menus owns what the server does with a hash. On disk an item is saved as a patch (ItemStack.MAP_CODEC’s “components”, with transient types silently dropped), but a block entity as a full DataComponentMap — the two are not symmetric.

The prototype, and why it is built at reload

A prototype is recorded once and built many times, and the two happen far apart in the program’s life. The recording is at class-init, long before a world exists. Bootstrap.bootStrap has loaded the Items class before BuiltInRegistries.bootStrap runs — the dispenser and cauldron bootstraps name items — and its static initialisers each build an Item, so every item in the game exists, and no item has components. An Item constructor registers its initializer in BuiltInRegistries.DATA_COMPONENT_INITIALIZERS and nothing more. Item.Properties.component and its convenience methods (Item.Properties.durability, Item.Properties.food, Item.Properties.equippable, Item.Properties.sword, …) only record a DataComponentInitializers.Initializer; nothing is a map yet. Item.Properties.delayedComponent and Item.Properties.delayedHolderComponent are the reason it must be deferred: they name registry entries that do not exist until a world’s registries do — an item’s DataComponents.JUKEBOX_PLAYABLE or DataComponents.DAMAGE_RESISTANT names an entry a data pack can change.

They are also rare. Between them the two methods have twenty-six call sites in the entire game, all inside Item and Items: fire resistance resolving DamageTypeTags.IS_FIRE, the banner patterns, the goat horn, the jukebox songs, the egg variants, the spear’s damage type, the axe, shovel and hoe’s block transformers, the pottery sherds, the trim materials and a handful more. Everything else is baked literal. Item.Properties.repairable is the near miss that shows where the line runs — it takes a tag, which sounds like context, and is still eager, because it asks BuiltInRegistries.acquireBootstrapRegistrationLookup at class-init and stores an unresolved HolderSet instead of waiting for one. So the deferral exists for twenty-six call sites, and everything else is deferred along with them because the map is built in one pass.

The maps are built with full registry context — tags, damage types, jukebox songs resolvable — by DataComponentInitializers.build, reached from ReloadableServerResources.loadResources on the reload worker on the server, and installed with Holder.Reference.bindComponents on the owning thread: on the server in ReloadableServerResources.updateComponentsAndStaticRegistryTags (by the thread driving a world load, and by the server thread on /reload), on the client in RegistryDataCollector at the end of configuration. A /reload therefore rebinds every item’s prototype. Every registry element gets a component map (DataComponentMap.EMPTY if it had no initializer), so EntityType holders have one too — with one asymmetry, and it runs the way round you would not guess. ClientConfigurationPacketListenerImpl passes Connection.isMemoryConnection into RegistryDataCollector.collectGameRegistries, which negates it, so a singleplayer client binds only the registries RegistrySynchronization.isNetworkable accepts and a multiplayer client binds every one. Singleplayer can skip the rest because it shares BuiltInRegistries with the integrated server, whose own apply has already bound them.

Before binding, reading a registry element’s components throws, and the failure is a null-check, not a friendly error: Item.components merely delegates, the throw comes from Holder.Reference.components, and the non-throwing question is Holder.areComponentsBound. Item.CODEC_WITH_BOUND_COMPONENTS guards on it and refuses to decode a stack until then.

Eleven entries in DataComponents.COMMON_ITEM_COMPONENTS make the map every item’s prototype starts from. Notably it puts an empty ItemEnchantments on every item, which is what ItemStack.isEnchantable depends on: it gates first on DataComponents.ENCHANTABLE being present, and then on DataComponents.ENCHANTMENTS being present and empty.

A prototype can carry a validator. DataComponentMap.Builder.addValidator is where a structural rule is installed, and Item.Properties installs one: an item may not be both damageable and stackable. The stack-time twin of that rule, and the rest of what ItemStack.validateStrict refuses, belongs to items and stacks.

The reverse index: DataComponentLookup

Every frozen MappedRegistry builds one (Registry.componentLookup): a lazily-populated reverse index answering “which elements carry this component value?”, which is how the game finds the spawn egg for an entity type or the item for a dye colour. It scans the bound prototypes the holders carry the first time a component type is asked for and keeps what it found, so before the first reload it throws, and a /reload that changes a prototype leaves its old answer in place.

The readers and the predicates

DataComponentGetter reads one component; DataComponentHolder reads one and has a map, and is implemented only by ItemStack, which is where DataComponentHolder.get, DataComponentHolder.getOrDefault and DataComponentHolder.has come from. ItemInstance is the read-only face over ItemStack and ItemStackTemplate that predicates take (items and stacks). Beyond item behaviour, the callers are loot functions (CopyComponentsFunction) and the /give, /item and /loot commands — which is where the square brackets this page opened on are finally turned into a patch: ItemParser reads the text component by component through each DataComponentType’s own codec (codecs, NBT and JSON), and what it produces is the same DataComponentPatch the wire and the disk carry.

The predicates come in two strengths. DataComponentExactPredicate requires every listed component to equal. The partial DataComponentPredicate family under core/component/predicates — DataComponentPredicates, 15 kinds, in their own BuiltInRegistries.DATA_COMPONENT_PREDICATE_TYPE registry — matches a shape rather than a value, and DataComponentMatchers joins the two for ItemPredicate.

The values, by package

packagevalue types
world/item/componentConsumable, Tool, Weapon, BlocksAttacks, ItemLore, CustomData, TooltipDisplay, ItemContainerContents, BundleContents, TypedEntityData …
world/item/equipmentEquippable
world/item/enchantmentItemEnchantments, Enchantable, Repairable

Most combat has left the Item subclasses. Weapon, BlocksAttacks, KineticWeapon, PiercingWeapon, AttackRange, SwingAnimation and Tool are components; Item.Properties.sword, Item.Properties.spear and Item.Properties.humanoidArmor build whole kits, and SwordItem is gone; MaceItem, BowItem, CrossbowItem and TridentItem are among the few that still carry their own. The tools that act on a block went the same way: stripping, path-making and tilling are DataComponents.BLOCK_TRANSFORMER, which Item.Properties.axe, Item.Properties.shovel and Item.Properties.hoe point at BlockTransformers.AXE, BlockTransformers.SHOVEL and BlockTransformers.HOE, and which Item.useOn runs for an item that carries it.

Sharpness onto a sword, and how the client is told

sequenceDiagram
    box transparent the server
    participant EM as EnchantmentMenu
    participant IStack as ItemStack
    participant PDM as PatchedData<br/>ComponentMap
    end
    box transparent the client
    participant CPL as ClientPacketListener
    end

    Note over EM: the server thread, the button-click packet has arrived
    EM->>IStack: enchant, once per EnchantmentInstance
    Note over EM,IStack: a book goes through ItemStack.transmuteCopy first: the same patch, a new prototype
    IStack->>IStack: EnchantmentHelper.<br/>updateEnchantments, then set
    IStack->>PDM: set: ensureMapOwnership clones the shared map, then patch.put
    Note over PDM: the patch is now one entry: enchantments to sharpness 3
    EM->>EM: broadcastChanges, still inside the handler — this slot does not match
    EM->>CPL: ClientboundContainerSetSlotPacket: count, item id, the patch
    Note over CPL: decoded on Netty, the patch laid over the client's own prototype
    CPL->>CPL: handleContainerSetSlot, then the menu's setItem

One write on the server and one slot packet for the sword, and what crosses is the diff alone. The client never receives the prototype: it rebuilds the stack by laying the patch over the copy it already has, which is why a component the server never changed costs nothing to send.

The menu owns the mutation. EnchantmentMenu.clickMenuButton runs under ContainerLevelAccess.execute on the server thread and calls ItemStack.enchant once per chosen EnchantmentInstance. (A book takes one step more first: ItemStack.transmuteCopy builds a new stack carrying the same patch over the enchanted book’s prototype.) ItemStack.enchant hands the edit to EnchantmentHelper.updateEnchantments, which reads the current ItemEnchantments, edits a mutable copy and writes the immutable result back with ItemStack.set — under DataComponents.STORED_ENCHANTMENTS for an Items.ENCHANTED_BOOK and DataComponents.ENCHANTMENTS for everything else, which is the one place the book branch reaches past the transmute; the enchanting rules, the lapis, the seed and /enchant are Part VII’s. What matters here is that enchantments are a value, not a list on the stack: one component, one write.

One write, one patch entry. ItemStack.set is PatchedDataComponentMap.set. The map the sword owned was shared with whatever it was copied from, so PatchedDataComponentMap.ensureMapOwnership clones it now; the new ItemEnchantments differs from the prototype’s empty one, so the patch gains its single entry. A book that was transmuted a moment earlier carries the same patch over a different prototype, which is the whole meaning of ItemStack.transmuteCopy.

The menu compares, and here it does not wait for the tick. A menu-button click, like an ordinary click, is answered inside the handler that accepted it (containers and menus), so the correction leaves at once. The sword’s slot no longer matches what the client was last told, so ClientboundContainerSetSlotPacket goes out, and only the patch crosses: ItemStack.OPTIONAL_STREAM_CODEC writes the count, Item.STREAM_CODEC (a registry id) and the patch. The client answers later clicks with a HashedStack of CRC32C checksums rather than stacks (codecs, NBT and JSON for the hashing, containers and menus for the click protocol).

The client rebuilds against its own prototype. The decoder constructs ItemStack from holder, count and patch, and that constructor is PatchedDataComponentMap.fromPatch against the client’s own bound prototype — the map RegistryDataCollector bound at the end of configuration, or in singleplayer the server’s own. That is the reason components must be bound on the client before the play phase: an unbound prototype would throw in a packet decoder on a Netty thread. ClientPacketListener.handleContainerSetSlot then hands the stack to AbstractContainerMenu.setItem, and the Sharpness line in the tooltip, when the sword is hovered, is ItemEnchantments.addToTooltip reading the same component.

Components on things that are not items

Block entities, both directions. Placing runs BlockEntity.applyComponentsFromItemStack, which hands subclasses a recording DataComponentGetter: whatever BlockEntity.applyImplicitComponents reads is forgotten from the patch (DataComponentPatch.forget) and only the leftovers persist as opaque BlockEntity.components. Two types are pre-seeded into that forget set regardless of whether anything reads them — DataComponents.BLOCK_ENTITY_DATA and DataComponents.BLOCK_STATE — and only the added half of the resulting patch is kept, so removals are discarded. Breaking or picking runs the reverse, BlockEntity.collectComponents over BlockEntity.collectImplicitComponents, with BlockEntity.removeComponentsFromTag de-duplicating what was promoted; BlockItem.setBlockEntityData is the write path for the opaque blob.

Entities, a view with a narrow write path. Entity implements DataComponentGetter with no patch of its own: Entity.get answers DataComponents.CUSTOM_NAME and DataComponents.CUSTOM_DATA by hand, lets subclass overrides (Sheep, Wolf, Villager …) answer the variant-shaped types, and otherwise falls through to its EntityType holder’s bound prototype — the same map every registry element gets. Entity.applyComponentsFromItemStack is the write path, and it is not only spawn eggs: any item-to-entity spawn, an arrow picking up its stack and a lingering potion’s cloud all take it.

Where to look

DataComponentType · DataComponents · DataComponentMap · PatchedDataComponentMap · DataComponentPatch · DataComponentInitializers · Holder.Reference · DataComponentLookup · Item.Properties · ItemStack · ItemInstance · ItemStackTemplate · EnchantmentMenu · AbstractContainerMenu · BlockEntity · Entity (the getter half)


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