<!-- https://minecraftdocs.dev/systems/items/enchantments — this page as markdown. The book's index: https://minecraftdocs.dev/llms.txt; the whole book in one file: https://minecraftdocs.dev/llms-full.txt -->

# Enchantments

> Verified against **Minecraft 26.3** · Part VII · A player hits a zombie with a Fire Aspect sword, and everything that makes the zombie burn is data.

You swing a Fire Aspect sword at a zombie and the zombie catches fire. Go
looking for the class that does that — the one with the *on hit, set them
alight* method — and there is nothing to find. There are no enchantment
subclasses. The forty-three vanilla enchantments are JSON files in the
built-in data pack, `Enchantments` is a bag of forty-three registry keys that only five files outside the data generator ever name, and the whole
*Fire Aspect is melee only* rule is **one loot condition**, sitting on one
entry of a `DataComponentMap` that no item ever holds.

That is the pattern the rest of the page is about. An enchantment is a
named modifier that holds no code; every behaviour in the game an enchantment can change is something another
system asks about at a well-defined moment: one of its component keys, a tag that
names it, or its id in loot data.

## The cast

| class | what it decides | thread |
|---|---|---|
| `Enchantment` | the record — a description, a definition, an exclusive set and a map of effects. No behaviour of its own | built at data-pack load, read on both sides |
| `Enchantment.EnchantmentDefinition` | what it can go on, what it costs, which slots it counts in | data-pack load |
| `EnchantmentEffectComponents` | the thirty-one keys, one per moment an effect can act | static, both sides |
| `EnchantmentHelper` | the static hook surface — walks stacks and slots, calls into the record, holds no state | Server for every entity and location effect; some entry points, two value effects among them, on the Render thread |
| `ConditionalEffect` / `TargetedConditionalEffect` | whether this effect fires here, and on whom | Server |
| `EnchantmentEntityEffect` | the thing that finally happens | Server only — the signature demands a `ServerLevel` |
| `ItemEnchantments` | the id-to-level map on the stack, under one of two components | both sides |
| `EnchantedItemInUse` | the stack, the slot it was found in, its owner and a break callback — the bundle an entity or location effect receives, with a slot that may be empty | Server |

## A record with a definition and a bag of components

`Enchantment` is a record of four things: a description `Component`, an
`Enchantment.EnchantmentDefinition`, a `HolderSet` of enchantments it is
exclusive with, and a `DataComponentMap` of effects. `Enchantment.getEffects`
looks a component type up in that map and returns an empty list if it is
absent, which is the whole dispatch mechanism.

The definition is where the enchanting rules live. It carries the supported
items and a *narrower, optional* set of primary items — both normally item
tags — so `Enchantment.isPrimaryItem` falls back to the supported set when
the second is absent, while `Enchantment.isSupportedItem` only ever asks the
first. It carries the weight, the maximum level, two `Enchantment.Cost`
curves (a base plus a per-level-above-first increment, read by
`Enchantment.Cost.calculate`), the anvil cost, and a list of
`EquipmentSlotGroup` — the slots in which this enchantment counts at all,
tested by `Enchantment.matchingSlot`. The codec bounds the weight to 1–1024
and the maximum level to 1–255, which is `Enchantment.MAX_LEVEL`;
`Enchantment.getMinLevel` is the one number that is not data, hardcoded to 1.

What a stack carries is not that record but `ItemEnchantments`, an
immutable id-to-level map under `DataComponents.ENCHANTMENTS` — or, for an
enchanted book's inert set, under `DataComponents.STORED_ENCHANTMENTS`. It
is changed only through `ItemEnchantments.Mutable`, whose
`ItemEnchantments.Mutable.upgrade` merges by maximum and clamps at 255.
`EnchantmentInstance`, a holder-and-level pair with a
`EnchantmentInstance.weight` delegate, is what the weighted selection path
is built on — and that path, along with the table, the anvil, the
grindstone, the providers and `/enchant`, is a separate machine with its own
arithmetic, on [enchanting](enchanting#the-five-paths-at-a-glance).

## Thirty-one keys, three registries and one curve

`EnchantmentEffectComponents` registers thirty-one component types into
`BuiltInRegistries.ENCHANTMENT_EFFECT_COMPONENT_TYPE`. Twenty-four hold a
list of `ConditionalEffect` (or `TargetedConditionalEffect`); the other
seven are plain — `EnchantmentEffectComponents.ATTRIBUTES`, two sound
lists, two unconditional values, and the two `Unit`-valued flags
`EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP` and
`EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE`, true by being present.

Three registries supply the effect objects, and two of the three overlap
almost entirely — which is the only thing about them worth remembering.
`EnchantmentValueEffect`, the odd one out, has six shapes and modifies a
running number
(`AddValue`, `MultiplyValue`, `SetValue`, `RemoveBinomial`,
`ScaleExponentially` and `AllOf.ValueEffects`). `EnchantmentEntityEffect`
has fifteen and does something to an entity — `Ignite`, `DamageEntity`,
`ApplyMobEffect`, `SummonEntityEffect`, `AllOf.EntityEffects` and ten more.
`EnchantmentEntityEffect` *extends* `EnchantmentLocationBasedEffect`, whose
registry has sixteen entries: those same fifteen ids plus one. That extra one
is *attribute*, `EnchantmentAttributeEffect`, which installs an `AttributeModifier`
([attributes](../entities/attributes#where-the-modifiers-come-from)) and is,
beside that registry's own *all_of* (`AllOf.LocationBasedEffects`), the only effect
that is location-based without also being an entity effect.

### The curve that scales a level

`LevelBasedValue` turns a level into a number, and six shapes are in its
dispatch registry (`LevelBasedValue.Linear`, `LevelBasedValue.Clamped`,
`LevelBasedValue.Fraction`, `LevelBasedValue.LevelsSquared`,
`LevelBasedValue.Exponent`, `LevelBasedValue.Lookup`).
`LevelBasedValue.Constant` is deliberately **not** among them: it is the
other arm of an either-codec, which is what makes a bare float legal
anywhere a curve is expected.

### And the condition twenty-four keys sit behind

`ConditionalEffect` is an effect plus an optional `LootItemCondition`.
`TargetedConditionalEffect` adds two `EnchantmentTarget` fields — which side
of the fight the enchantment *lives on* (`TargetedConditionalEffect.enchanted`)
and which side it *lands on* (`TargetedConditionalEffect.affected`) —
except for the equipment-drops variant, whose codec reads only the first and
pins the second to `EnchantmentTarget.VICTIM`.

Both implement `Validatable`, and the effect codecs are one of only two places
in the game where a context mismatch is a **hard error at decode time** rather
than a logged warning. The other is `VillagerTrade`, and nothing else in the
game gets the treatment ([contexts and
predicates](contexts-and-predicates#three-ways-a-parameter-can-be-missing)
owns the three ways a parameter goes missing and the two that are fatal).
The consequence here is worth the trip: a *post_attack* effect asking about a
block state fails to load, rather than failing quietly at runtime a thousand
hits later.

## Seven families of moment

Everything above does nothing until something calls `EnchantmentHelper`. The enchantment package is called from all over the game, so the artefact worth keeping is a table of who calls what: every entry point with
its callers is [the enchantment hook
table](../../reference/enchantment-hooks), and these are the seven kinds of moment its effect hooks fall into; the rest of the table stores, looks up or chooses enchantments.

| family | a representative hook or two | who makes it real |
|---|---|---|
| damage and protection | `EnchantmentHelper.modifyDamage`, `EnchantmentHelper.getDamageProtection` | `ServerPlayer.getEnchantedDamage`, `LivingEntity.getDamageAfterMagicAbsorb` |
| post-attack effects | `EnchantmentHelper.doPostAttackEffectsWithItemSource`, `EnchantmentHelper.doPostPiercingAttackEffects` | `Player.itemAttackInteraction`, `LivingEntity.postPiercingAttack` |
| durability and drops | `EnchantmentHelper.processDurabilityChange`, `EnchantmentHelper.processEquipmentDropChance` | `ItemStack.hurtAndBreak`, `Mob.dropCustomDeathLoot` |
| projectiles and the weapon in hand | `EnchantmentHelper.processProjectileCount`, `EnchantmentHelper.getPiercingCount` | `ProjectileWeaponItem.draw`, the `AbstractArrow` constructor |
| location and tick effects | `EnchantmentHelper.runLocationChangedEffects`, `EnchantmentHelper.tickEffects` | `LivingEntity.onChangedBlock`, `LivingEntity.baseTick` |
| experience and repair | `EnchantmentHelper.processBlockExperience`, `EnchantmentHelper.modifyDurabilityToRepairFromXp` | `Block.tryDropExperience`, `ExperienceOrb.repairPlayerItems` |
| the flag questions | `EnchantmentHelper.has`, `EnchantmentHelper.hasTag` | `ArmorSlot.mayPickup` for Curse of Binding, four blocks for the four *prevents* tags |

The location row is the one that keeps state:
`LivingEntity.activeLocationDependentEnchantments` remembers which
location-based effects are running in which slot, so the system can tell
*became active* from *still active*, and so an attribute effect can be taken
off cleanly by `EnchantmentHelper.stopLocationBasedEffects` when the armour
comes off.

## How one hook fires

Most hooks in those seven rows are the same shape underneath. A system reaches a
moment and asks `EnchantmentHelper`; the helper picks which stacks to walk,
filtering by slot if it was given one; the record picks which effect entries
apply; a loot condition decides whether this one fires. Only then does
anything happen. The flag row is the exception and is the reason the shape is
worth drawing: `EnchantmentHelper.has` and `EnchantmentHelper.hasTag` build no context and run no
condition: the first asks the record whether the key is present at all, the second
whether the enchantment is in a tag.

```mermaid
flowchart TD
    Caller["a system reaches a moment"] --> EH{"which EnchantmentHelper entry point"}
    EH -- "EnchantmentHelper.has, EnchantmentHelper.hasTag" --> Flag["ask the record or its tags: is it there at all"]
    EH -- "any of the other six" --> Walk["walk one stack, or every EquipmentSlot of one entity"]
    Walk --> Slot["on the slot-aware walk, keep entries Enchantment.matchingSlot accepts"]
    Slot --> Comp["read the list under one EnchantmentEffectComponents key"]
    Comp --> Target["for the targeted keys, keep entries whose enchanted target matches"]
    Target --> Ctx["build a LootContext on that hook's parameter set"]
    Ctx --> Cond{"ConditionalEffect.matches runs the LootItemCondition"}
    Cond -- "no" --> Drop["nothing happens"]
    Cond -- "yes" --> Apply["fold a value, or run the effect on the affected entity"]
```

*One shape, and the flag row's way round it. The flag row answers from the
record and its tags alone, with no context built and no condition run.*

The `LootContext` in the middle is the same machinery loot tables and
`/execute if predicate` use, and `Enchantment` builds five of them —
`Enchantment.damageContext` plus four private siblings for items, entities,
locations and block hits, one per parameter set the components name.
[Contexts and predicates](contexts-and-predicates#who-asks-and-with-which-set)
owns that half.

## Fire Aspect, from the click to the flame

Fire Aspect as data is one small JSON file: supported and primary items
are tags, weight 2, maximum level 2, anvil cost 4, dynamic cost curves, one
slot (*mainhand*), and **one effect** — a
`EnchantmentEffectComponents.POST_ATTACK` entry whose enchanted target is
the attacker, whose affected target is the victim, whose effect is `Ignite`
with a linear duration of four seconds per level, behind one damage-source
predicate requiring a *direct* hit.

```mermaid
sequenceDiagram
    participant SGPL as ServerGamePacket<br/>ListenerImpl
    participant Player as Player
    participant EH as EnchantmentHelper
    participant Ench as Enchantment
    participant Ignite as Ignite
    participant Entity as Entity

    SGPL->>Player: attack, once handleAttack has passed the range checks
    Player->>Player: createAttackSource, whose direct and causing entity are one
    Player->>EH: doPostAttackEffects<br/>WithItemSource,<br/>on a hit that landed
    EH->>EH: the victim's whole equipment first, then the attacker's main hand
    EH->>Ench: doPostAttack for the main-hand stack, in the ATTACKER pass
    Ench->>Ench: TargetedConditional<br/>Effect.matches asks <br/>if the hit was direct
    Ench->>Ignite: apply, with the victim as the affected target
    Ignite->>Entity: igniteForSeconds, raised only if the new value is larger
    rect rgba(0, 0, 0, 0.04)
        Note over Ignite,Entity: a later tick, and every tick after it while the fire lasts
        Entity->>Entity: baseTick sets the shared flags byte through SynchedEntityData
        Note over Entity: the flame travels as a data packet, the enchantment never does
    end
```

*Six lanes between the click and the flame. The band is where the enchantment has already finished —
what the other player sees is a `SynchedEntityData` flag, and one point of damage a
second is the entity's own business.*

**The swing, and the source that is the whole melee rule.**
`ServerGamePacketListenerImpl.handleAttack` runs the range checks and calls
`Player.attack` — unless the main-hand item has
`DataComponents.PIERCING_WEAPON`, in which case the packet is dropped and
none of this happens (see [the spear](../player/the-spear)
for its own path). `Player.createAttackSource` then asks
`ItemStack.getDamageSource`, and every branch of it reaches the
single-entity `DamageSource` constructor — the one that sets the direct
entity and the causing entity to the same object, making
`DamageSource.isDirect` true. An arrow's source has an arrow as the direct
entity and a player as the causing entity, so it is false. That one
comparison, read by one loot condition in one JSON file, is why Fire Aspect
never fires through a bow.

**The damage, then the hook.** `ServerPlayer.getEnchantedDamage` — the
override, not `Player`'s base version, which returns its argument unchanged
— folds Sharpness and friends in through `EnchantmentHelper.modifyDamage`,
and the hit goes through `Entity.hurtOrSimulate` to `LivingEntity.hurtServer`
([damage and death](../entities/damage-and-death#one-number-eight-steps-and-no-step-that-knows-another)). Only if that returned
true does `Player.itemAttackInteraction` call
`EnchantmentHelper.doPostAttackEffectsWithItemSource`.

**The one method has three branches.** The helper walks the *victim's* whole
equipment first, with `EnchantmentTarget.VICTIM` as the pass — that is
Thorns' lane, and why Thorns works from a chestplate while Fire Aspect does
not work from boots. Then, for a living causing entity, it walks **the one weapon stack it was
handed**, in the `EnchantmentTarget.ATTACKER` pass, keeping enchantments whose
declared slots include the main hand. Its third branch
takes a causing entity that is *not* living: a slotless walk with no filter at
all, which only fires when a break callback was supplied, and whose only
vanilla caller is `ThrownTrident`. **An attacker's armour can never
contribute a post-attack effect.**

That second branch is worth one more sentence, because the slot it names is a
label rather than a fact. It hands `EquipmentSlot.MAINHAND` to the filter
whatever the weapon actually is, and so does
`EnchantmentHelper.doPostPiercingAttackEffects`; `KineticWeapon.damageEntities`
reaches `Player.stabAttack` (a mob's `LivingEntity.stabAttack`) with whichever
slot the *use* was in. An
off-hand spear's enchantments are therefore tested against the main-hand slot
group. The slot question itself is asked two ways: the two
`EnchantmentHelper.forEachModifier` overloads test different things — the
`EquipmentSlot` one asks `Enchantment.matchingSlot`, the `EquipmentSlotGroup`
one asks whether the definition declares that exact group — and
`ItemStack.forEachModifier` uses one of each. One more pair like it is worth
knowing before you grep: the instance method is spelled
`Enchantment.modifyArmorEffectivness`, Mojang's typo, while the helper beside
it is `EnchantmentHelper.modifyArmorEffectiveness`.

**The condition and the target.** `Enchantment.doPostAttack` keeps the
entries whose enchanted target matches the pass, builds the
enchanted-damage context — victim, level, origin, damage source, and both
attacking entities as optional parameters — and runs the predicate. The
*affected* field then picks who receives the effect: attacker, direct
entity, or victim. Fire Aspect says victim, and if that works out to null
the effect is dropped in silence.

**The burn.** `Ignite.apply` calls `Entity.igniteForSeconds`, which floors
to ticks and hands `Entity.igniteForTicks` a number it applies **only if it
is larger than the counter already there** — while clearing any freeze
regardless, so re-hitting a burning target with a weaker Fire Aspect does
nothing except thaw it. No damage happens here. `Entity.baseTick` deals it,
one point every twentieth tick, on a `ServerLevel`, skipped in lava and
replaced by `Entity.clearFire` for a fire-immune entity. The same method
sets shared flag zero through `Entity.setSharedFlagOnFire`
([synched entity
data](../entities/synched-entity-data#five-more-channels-all-keyed-by-the-same-entity-id)),
which becomes
`Entity.displayFireAnimation` on the client — whose own `Entity.baseTick`,
finding no `ServerLevel`, clears the fire counter instead of burning.

## Three famous ones driven from the other end

The three enchantments players talk about most are driven from the other end:
something *else* finds each one on the stack and does the famous work, and only
one of the three has no effect component at all.

Fortune lives in the loot table. `ApplyBonusCount` and
`BonusLevelTableCondition` read the *tool* parameter out of a `LootContext`
and call `EnchantmentHelper.getItemEnchantmentLevel` on it — a level, not an
effect. Looting is the same trick twice more:
`EnchantedCountIncreaseFunction` and
`LootItemRandomChanceWithEnchantedBonusCondition` both read the *attacking
entity* parameter and call `EnchantmentHelper.getEnchantmentLevel`, which walks the slots of a `LivingEntity` the enchantment counts in and keeps the
best. Fortune has no effect component whatsoever, and Looting's only one is an
*equipment_drops* entry, which raises the chance a mob drops its equipment and
plays no part in its loot table
([loot tables](loot-tables#one-roll-drawn)).

Mending inverts it once more: `ExperienceOrb.repairPlayerItems` asks
`EnchantmentHelper.getRandomItemWith` for a damaged stack carrying
`EnchantmentEffectComponents.REPAIR_WITH_XP`, so the **orb** drives the repair
and the item is found. What runs on the item is one number: its
`EnchantmentEffectComponents.REPAIR_WITH_XP` entry, a multiply by two that
`EnchantmentHelper.modifyDurabilityToRepairFromXp` applies, makes each point of
experience buy two points of durability.

## What runs on the client, and why no entity effect does

No entity or location *effect* can: `EnchantmentEntityEffect` and
`EnchantmentLocationBasedEffect` both demand a `ServerLevel` in their
signatures, which is the same mechanical guarantee the loot system uses. But
two *values* do. `Enchantment.modifyUnfilteredValue` takes only a
`RandomSource`, and its two users are `Enchantment.modifyCrossbowChargeTime`
and `Enchantment.modifyTridentSpinAttackStrength`.

Both are visible as gameplay. `CrossbowItem.getChargeDuration` is called by
three entity renderers, by `FirstPersonHandsAndItems` and by the `CrossbowPull` item
property, so **Quick Charge is evaluated on the Render thread every frame a
crossbow is being drawn** — an enchantment hook in the frame loop, to pose an arm and pick one of three
textures. And `MultiPlayerGameMode.releaseUsingItem` runs
`TridentItem.releaseUsing` on the client's own copy, which asks
`EnchantmentHelper.getTridentSpinAttackStrength`, so **Riptide's strength is
computed client-side too**, which is what lets the riptide push be predicted
at all ([using an item](using-an-item#the-two-endings)).

The rest is drawing, or a question with no effect behind it. The drawing is
`ItemEnchantments.addToTooltip` for the tooltip, `ItemStack.hasFoil` for the
glint, and `EnchantmentHelper.forEachModifier` for the attribute lines, which
means the client evaluates the `LevelBasedValue` curve itself. The questions are Curse of Binding's flag, which
`ArmorSlot.mayPickup` and `Equippable.swapWithEquipmentSlot` ask while a click or
a use is predicted, and Riptide's sound, which
`EnchantmentHelper.pickHighestLevel` chooses before the `ServerLevel` test. And
the anvil and the grindstone build their results on the client's copy of the menu
too, with the same helpers the server uses. That is what the cast's *some entry
points on the Render thread* covers.

## Where the forty-three live, and when they are read

At world load, and not again. `Registries.ENCHANTMENT` and
`Registries.ENCHANTMENT_PROVIDER` are both in
`RegistryDataLoader.WORLD_REGISTRIES`, the dynamic registries built once
when a world opens ([identifiers and
registries](../foundations/identifiers-and-registries#when-a-world-opens)) —
not in `RegistryLayer.RELOADABLE`, where the loot tables and predicates live.
**`/reload` rebuilds those and every recipe and leaves the enchantments themselves
as the world found them**, their tags aside, which it re-reads with every other
registry's; changing an enchantment takes a restart. That is the sharpest
difference between this part's three engines, and the one a data-pack author
meets first.

What crosses the wire is usually an id and nothing else.
`Registries.ENCHANTMENT` is in `RegistryDataLoader.SYNCHRONIZED_REGISTRIES`
with the full `Enchantment.DIRECT_CODEC`, but
`RegistrySynchronization.packRegistry` sends a bare `Identifier` for every
element whose pack the client already has — for a vanilla client against a
vanilla server, all forty-three. Full definitions cross only for a data pack's
custom or overridden enchantments. Beyond that,
`ClientboundUpdateTagsPacket` for `EnchantmentTags` and, per stack, the
`DataComponents.ENCHANTMENTS` component: registry ids and levels.

## One JSON file, four effect objects, no Java

`Enchantments.LUNGE` is the only user of
`EnchantmentEffectComponents.POST_PIERCING_ATTACK` in the game, and it is the
whole pattern in one file. Its single effect is an `AllOf.EntityEffects` of
four — a `ChangeItemDamage`, an `ApplyExhaustion` scaled per level, an
`ApplyEntityImpulse` forward with its vertical component scaled away, and a
`PlaySoundEffect` holding three sounds, indexed by level rather than shuffled
— behind a four-clause condition checking that the user is not riding, not
elytra-flying, not in water, and either not a player, in creative, or fed.

Four effect objects, four predicates, no Java, and a lunge that shoves you
forward and tires you out. That is what an enchantment is.

## Questions players ask

**Why is an enchanted book inert?** Because every effect hook reads `DataComponents.ENCHANTMENTS` and nothing else,
through the private walks (`EnchantmentHelper.runIterationOnItem` among them) or,
for Mending's `EnchantmentHelper.getRandomItemWith`, directly. A book's set lives
under `DataComponents.STORED_ENCHANTMENTS`, and the routing between the two
is keyed on the exact item `Items.ENCHANTED_BOOK`
([enchanting](enchanting#the-one-question-all-five-ask) owns that routing).
The *component* is not so exclusive: `ItemStack` puts
`DataComponents.STORED_ENCHANTMENTS` in any stack's tooltip, and the anvil
prices its right-hand input as a book by testing for that component rather
than for the item ([enchanting](enchanting#what-each-path-is-allowed-to-add))
— so a data pack can make a stick priced like one, but never make it behave
like one.

**Does Fire Aspect cook the loot?** Not through the enchantment. The cooking
is `SmeltItemFunction`, an ordinary loot function on the mob's own table,
behind a condition that passes when the victim is on fire *or* the direct
attacker's main hand carries an enchantment in `EnchantmentTags.SMELTS_LOOT` —
a tag whose only member is Fire Aspect ([loot
tables](loot-tables#one-roll-drawn)). So the sword you swung is read twice
by two systems that know nothing about each other: once for its effect
component, and once, much later, as a name in a tag.

## Where to look

`Enchantment` is four fields and will take ten minutes; read it first, because
everything else on this page is about how those four fields are consulted.
`Enchantment.EnchantmentDefinition` inside it is the enchanting rules, and
`EnchantmentEffectComponents` beside it is the list of thirty-one moments.

Then `EnchantmentHelper`, which is the entire behaviour surface and holds no
state. Read the private `EnchantmentHelper.runIterationOnItem` and its slot-aware
siblings before any public entry point: they are the walk, and most hooks above
them are a thin wrapper around one of them. [The hook
table](../../reference/enchantment-hooks) is the map of which callers
reach which.

For the effect side, `ConditionalEffect` and `TargetedConditionalEffect` are the two shapes a
conditional entry takes, and `Ignite` is the smallest effect
worth reading — three lines that set an entity on fire. `LevelBasedValue` is the curve underneath most of them.

For what a stack carries rather than what an enchantment is,
`ItemEnchantments`; for the selection machinery this page deliberately leaves
alone, `EnchantmentInstance` and then [enchanting](enchanting#the-five-paths-at-a-glance).

---

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