Skip to main content

Register Enchantments

Enchantments in NeoForge 1.21+ are data-driven. RegistryLib provides an .enchantment() builder that generates the enchantment JSON, lang entries, and tag entries from code, with no hand-written JSON needed. The builder uses the Minecraft Enchantment.Builder API with typed DataComponentType references.

Simple Enchantment (Vanilla Effects)

Use vanilla effect components (e.g. EnchantmentEffectComponents.BLOCK_EXPERIENCE); no custom DataComponentType registration needed.

import net.minecraft.tags.EnchantmentTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.EquipmentSlotGroup;
import net.minecraft.world.item.enchantment.EnchantmentEffectComponents;
import net.minecraft.world.item.enchantment.LevelBasedValue;
import net.minecraft.world.item.enchantment.effects.AddValue;

public static final EnchantmentEntry ORE_FORTUNE = REGISTRYLIB
.enchantment("ore_fortune")
.lang("Ore Fortune")
.lang(LANG_ZH_CN, "矿石财运")
.supportedItems(ItemTags.MINING_ENCHANTABLE)
.weight(5)
.maxLevel(3)
.minCost(15, 9)
.maxCost(65, 9)
.anvilCost(4)
.slots(EquipmentSlotGroup.MAINHAND)
.addTag(EnchantmentTags.IN_ENCHANTING_TABLE)
.withEffect(EnchantmentEffectComponents.BLOCK_EXPERIENCE,
new AddValue(LevelBasedValue.perLevel(1.0f, 1.0f)))
.register();

This single declaration:

  1. Generates data/<modid>/enchantment/ore_fortune.json during datagen.
  2. Registers English and Chinese lang entries.
  3. Adds the enchantment to minecraft:in_enchanting_table.
  4. Returns an EnchantmentEntry with .getKey() for code references.

Full Enchantment (Custom Effect Component)

For custom behavior (e.g. auto-smelting), first register a DataComponentType, then use .withEffect() in the builder.

1. Define the Effect Record

public record AutoSmeltEffect(float chancePerLevel) {
public static final MapCodec<AutoSmeltEffect> CODEC = RecordCodecBuilder.mapCodec(
inst -> inst.group(
Codec.FLOAT.optionalFieldOf("chance_per_level", 1.0F)
.forGetter(AutoSmeltEffect::chancePerLevel))
.apply(inst, AutoSmeltEffect::new));
}

2. Register the DataComponentType

Use dataComponentTypeEntry(...) when another API can accept a supplier or lazy registry entry. It avoids resolving your mod-registered component during static initialization:

public static final DataComponentTypeEntry<List<ConditionalEffect<AutoSmeltEffect>>>
AUTO_SMELT_EFFECT = REGISTRYLIB.dataComponentTypeEntry(
"auto_smelt",
Registries.ENCHANTMENT_EFFECT_COMPONENT_TYPE,
builder -> builder.persistent(
ConditionalEffect.codec(AutoSmeltEffect.CODEC.codec()).listOf()));

Use dataComponentType(...) instead when you specifically need the concrete DataComponentType value immediately.

3. Register with the Enchantment Builder

public static final EnchantmentEntry AUTO_SMELT = REGISTRYLIB
.enchantment("auto_smelt")
.lang("Auto Smelt")
.lang(LANG_ZH_CN, "自动熔炼")
.supportedItems(ItemTags.MINING_ENCHANTABLE)
.weight(2)
.maxLevel(1)
.minCost(25, 25)
.maxCost(75, 25)
.anvilCost(8)
.slots(EquipmentSlotGroup.MAINHAND)
.addTag(EnchantmentTags.IN_ENCHANTING_TABLE)
.withEffect(AUTO_SMELT_EFFECT, new AutoSmeltEffect(1.0f))
.register();
tip

For vanilla effect components like EnchantmentEffectComponents.BLOCK_EXPERIENCE, pass the component directly because it is already a static constant. For mod-registered effect components, prefer the lazy DataComponentTypeEntry form when the receiving API supports it.

EnchantmentBuilder API

MethodPurpose
enchantment(name)Start enchantment builder (returns EnchantmentBuilder)
.lang(name)Set English display name
.lang(providerType, name)Set localized name for a specific lang provider
.lang(Map<String, String>)Set display names for multiple locales in one call
.supportedItems(TagKey<Item>)Set supported item tag (e.g. ItemTags.MINING_ENCHANTABLE)
.primaryItems(TagKey<Item>)Set primary items (enchanting table preference)
.weight(n)Set enchantment weight (rarity)
.maxLevel(n)Set maximum enchantment level
.minCost(base, perLevel)Set minimum enchanting cost
.maxCost(base, perLevel)Set maximum enchanting cost
.anvilCost(n)Set anvil cost
.slots(EquipmentSlotGroup...)Set equipment slots (e.g. EquipmentSlotGroup.MAINHAND)
.exclusiveWith(ResourceKey<Enchantment>...)Set mutually exclusive enchantments
.withEffect(type, effect)Add a conditional effect (vanilla DataComponentType)
.withEffect(supplierOrEntry, effect)Add a conditional effect through a lazy supplier or entry wrapper
.withEffect(type, effect, condition)Add a conditional effect with loot condition
.withSpecialEffect(type, effect)Add a non-list effect component
.configure(consumer)Direct access to Enchantment.Builder
.addTag(TagKey<Enchantment>...)Add to enchantment tags (e.g. EnchantmentTags.IN_ENCHANTING_TABLE)
.register()Register and return EnchantmentEntry
.build()Register and return parent (for chaining)

Enchantment JSON Fields (Generated)

FieldDescription
descriptionTranslatable text component for the name
supported_itemsItem tag that can carry this enchantment
weightRarity weight (higher = more common)
max_levelMaximum enchantment level
min_cost / max_costEnchanting table cost range
anvil_costAnvil cost in levels
slotsEquipment slots (mainhand, offhand, head, etc.)
effectsMap of effect component type -> effect definition list
important

Enchantments are data-driven in NeoForge 1.21+. The .enchantment() builder generates the JSON automatically during datagen; you do not need to write JSON files manually.

warning

Calling .register() on the same EnchantmentBuilder twice throws an IllegalStateException. Store the returned EnchantmentEntry in a static final field and reuse it instead of re-registering.

See Also