Minecraft 1.21 replaced the Java-class-based enchantment system with a fully data-driven one. Custom enchantments are now defined entirely in JSON files (or generated via datagen), with no Java subclass required for most behaviours. Built-in effect types cover the vast majority of useful enchantment mechanics, and a custom Java effect type is only needed for genuinely novel logic that cannot be composed from existing types.
What Changed in 1.21
Before 1.21, you extended the Enchantment class and overrode methods like getDamageBonus and doPostAttack. That API no longer exists. In 1.21.1:
- Enchantments are registered as datapack built-in entries, similar to biomes and structures.
- Each enchantment JSON describes its level range, cost formula, applicable items, and a set of effect components.
- Effect components define what the enchantment does: add damage, apply a mob effect, spawn particles, explode, and so on.
- Exclusive sets prevent incompatible enchantments from being combined on the same item.
The enchantment is still a registry object and still has a ResourceKey<Enchantment>, but the implementation is driven by data rather than Java logic.
The Enchantment JSON
Create the file src/main/resources/data/examplemod/enchantment/healing_strike.json in your common project. This "Healing Strike" enchantment applies Regeneration to the attacker for a short time whenever they hit a mob, scaling duration with enchantment level:
Key fields explained:
- description: The display name, as a raw text component. Using
translatedefers to the lang file. - exclusive_set: A tag of enchantments that cannot coexist with this one on the same item. Using the vanilla
#minecraft:exclusive_set/damagetag prevents combining Healing Strike with Sharpness, Smite, and Bane of Arthropods. - supported_items: The tag of items this enchantment can appear on at all. Used by the enchanting table and the
/enchantcommand. - primary_items: The subset of supported items the enchanting table prefers. Items in this tag are weighted higher in the enchanting table pool.
- weight: How often this enchantment appears in the enchanting table. Higher weight means more frequent. Vanilla common enchantments use 10; rarer ones use 1-5.
- min_cost / max_cost: The required enchanting table level range, calculated as
base + (level - 1) * per_level_above_first. - anvil_cost: The experience cost in an anvil to apply or combine this enchantment.
- slots: Which equipment slots activate the enchantment effect. Common values are
mainhand,offhand,head,chest,legs,feet,armor,hand.
Effect Triggers and Built-in Effects
The effects object maps trigger keys to lists of conditional effects. Each trigger fires at a specific point in game logic:
minecraft:post_attack: After the enchanted item hits an entity. Provides"affected"to target the attacker, victim, or damaging entity.minecraft:damage: Adds bonus damage to each hit.minecraft:hit_block: When a block is hit with the item.minecraft:equipment_drops: Modifies the drop chance of equipment when the carrier dies.minecraft:tick: Fires every tick while the item is in the relevant slot.minecraft:projectile_spawned: After a projectile is fired from the enchanted bow or crossbow.
Within each trigger, the available effect types include:
minecraft:apply_mob_effect: Applies one or more mob effects to the target entity.minecraft:damage_item: Deals extra durability damage to the item.minecraft:explode: Creates an explosion at the target's position.minecraft:ignite: Sets the target on fire for a number of seconds.minecraft:play_sound: Plays a sound at the target's position.minecraft:spawn_particles: Spawns a particle effect.minecraft:summon_entity: Summons an entity at the target's position.minecraft:all_of: Composes multiple effects into one, all applied together.
Level-scaling values accept either a plain float (constant across all levels) or a LevelBasedValue object. The most common types are:
Registering the Resource Key
Even though the enchantment is defined in JSON, you still need a ResourceKey<Enchantment> in Java so you can reference the enchantment at runtime (for example, to check whether an item has it, or to give it to the player in a loot table). Create an EnchantmentKeys class in your common project:
To retrieve a live Holder<Enchantment> at runtime for use in code (such as checking enchantment level on an item), look it up from the level's registry:
Datagen via DatapackBuiltinEntriesProvider
Instead of writing the JSON by hand, you can generate it with DatapackBuiltinEntriesProvider. This is recommended for large mods because it keeps the enchantment definition type-safe and co-located with the rest of your datagen code.
First, create a bootstrap method that registers the enchantment using the Java builder API:
Then wire it into your existing DatapackBuiltinEntriesProvider in the NeoForge GatherDataEvent handler. Add Registries.ENCHANTMENT to the set of registries to generate and pass ExampleEnchantmentBootstrap::bootstrap as the population function:
Custom Effect Types
If none of the built-in effect types cover your desired behaviour, you can register a custom one. A custom entity effect must implement EnchantmentEntityEffect and provide a MapCodec for JSON serialisation:
Register the codec in a new registry provider:
Then reference it in your enchantment JSON by its registry name:
Lang Entry
Add a single translation entry for the enchantment name. The key format is enchantment.<namespace>.<path>:
The description in the JSON uses { "translate": "enchantment.examplemod.healing_strike" }, so this lang key must exist for the name to appear correctly in the enchanting table and on items.
You can also grab this directly from the EnchantmentKeys file where we declared a ResourceKey earlier.
Testing
Use /enchant @s examplemod:healing_strike 3 to apply the enchantment to your held sword at level III. The command will fail if the enchantment JSON is not found or contains a syntax error; check the server log for a more specific error message in that case.
Hit a mob and confirm the Regeneration effect appears on your character. Use /effect give @s minecraft:regeneration 0 first to clear any existing Regeneration, then strike a mob and watch the effect bar for the newly applied one.
To test that the enchanting table respects the weight and level range, place a max-level enchanting table (surrounded by bookshelves) and enchant a sword repeatedly. With a weight of 3, Healing Strike should appear occasionally but less frequently than common enchantments like Sharpness (weight 10). Verify it does not appear on non-sword items by trying to enchant a pickaxe.
You can find the source for this tutorial here:
View Source on GitHubCustom Commands (MultiLoader 1.21+)
Build a Brigadier command tree with literal and argument nodes, handle permission levels via CommandSourceStack, use vanilla argument types including EntityArgument and BlockPosArgument, and register the dispatcher on both loaders.
Continue →