MultiLoader 26.1+ · Part 13

Data Generation: Enchantments (MultiLoader 26.1+)

INTERMEDIATE MULTILOADER 26.1-26.1.2 18 min read · Jun 14, 2026
MultiLoader 26.1+ · 17 parts
1 Getting Started with MultiLoader 26.1+ 2 Setting Up RegistrationUtils (MultiLoader 26.1+) 3 Creating Items (MultiLoader 26.1+) 4 Creating Blocks (MultiLoader 26.1+) 5 Data Generation: Block & Item Models (MultiLoader 26.1+) 6 Data Generation: Block Loot Tables (MultiLoader 26.1+) 7 Data Generation: Crafting Recipes (MultiLoader 26.1+) 8 Data Generation: Block & Item Tags (MultiLoader 26.1+) 9 Data Generation: Language Files (MultiLoader 26.1+) 10 Data Generation: Advancements (MultiLoader 26.1+) 11 Data Generation: Sound Definitions (MultiLoader 26.1+) 12 Data Generation: Particle Descriptions (MultiLoader 26.1+) 13 Data Generation: Enchantments (MultiLoader 26.1+) 14 Custom Food Items (MultiLoader 26.1+) 15 Custom Tools (MultiLoader 26.1+) 16 Custom Armour (MultiLoader 26.1+) 17 Block Entities (MultiLoader 26.1+)

In 26.1, enchantments are no longer hard-coded Java objects — they are datapack registry entries, defined entirely in JSON and bootstrapped at startup. This means the datagen system IS how you create a custom enchantment: you define it in Java using a builder-style bootstrap and the generator writes the JSON file. There is no separate runtime registration step.

NOTE
Complete Data Generation: Crafting Recipes before continuing. We add a new provider to the existing gatherData listener.

How Enchantments Work in 26.1

Enchantments live in a datapack registry (Registries.ENCHANTMENT). Unlike static registries (items, blocks), datapack registries are populated from JSON files at world load, so they exist only after the lookup provider is resolved. NeoForge exposes them through DatapackBuiltinEntriesProvider, which takes a RegistrySetBuilder and writes one JSON file per entry you register.

The actual behaviour of an enchantment (what it does when applied) is controlled by EnchantmentEffectComponents passed to the constructor. This tutorial focuses on defining the enchantment structure and getting the JSON generated. Effect components are covered in a dedicated enchantments tutorial later in the series.

Enchantment Bootstrap

Create a class called ExampleEnchantmentBootstrap in your common module. It declares the ResourceKey for the enchantment and the bootstrap method that populates the registry:

java
public class ExampleEnchantmentBootstrap {
public static final ResourceKey<Enchantment> EXAMPLE_ENCHANTMENT =
ResourceKey.create(Registries.ENCHANTMENT,
Identifier.fromNamespaceAndPath(Constants.MOD_ID, "example_enchantment"));
public static void bootstrap(BootstrapContext<Enchantment> context) {
HolderGetter<Item> items = context.lookup(Registries.ITEM);
context.register(EXAMPLE_ENCHANTMENT, new Enchantment(
Component.translatable("enchantment.examplemod.example_enchantment"),
new EnchantmentDefinition(
items.getOrThrow(ItemTags.SWORD_ENCHANTABLE),
Optional.of(items.getOrThrow(ItemTags.SWORD_ENCHANTABLE)),
10, // weight in the enchanting table
3, // max level
Enchantment.dynamicCost(1, 10), // min cost per level
Enchantment.dynamicCost(20, 10), // max cost per level
2, // anvil cost multiplier
List.of(EquipmentSlotGroup.MAINHAND)
),
HolderSet.empty(), // incompatible enchantments
DataComponentMap.EMPTY // effect components (none yet)
));
}
}

Breaking down the key parameters:

  • supportedItems — the tag of items that can receive this enchantment in the enchanting table. ItemTags.SWORD_ENCHANTABLE allows swords.
  • primaryItems — items for which this enchantment is considered "primary" (higher weight when randomly selected). Wrap in Optional.of, or pass Optional.empty() to fall back to supportedItems.
  • weight — how frequently the enchantment appears. Vanilla common enchantments use 10, rare ones use 1–2.
  • dynamicCost(base, perLevel) — the experience level cost formula. dynamicCost(1, 10) gives a cost of 1 at level 1, 11 at level 2, 21 at level 3.
  • incompatible enchantments — a HolderSet of enchantments that cannot appear on the same item. Pass HolderSet.empty() for none.
TIP
To look up the tag for items that accept a given tool's enchantments, search for ItemTags in the Minecraft source. Common values are SWORD_ENCHANTABLE, BOW_ENCHANTABLE, ARMOR_ENCHANTABLE, MINING_ENCHANTABLE, and TRIDENT_ENCHANTABLE.

Registering via DatapackBuiltinEntriesProvider

Open gatherData and add the provider. It takes a RegistrySetBuilder that maps each datapack registry to its bootstrap method, and a set of mod IDs whose entries should be included in the output:

java
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
CompletableFuture<HolderLookup.Provider> registries = event.getLookupProvider();
generator.addProvider(true,
new LootTableProvider(output, Set.of(),
List.of(new LootTableProvider.SubProviderEntry(
ExampleBlockLootTableProvider::new,
LootContextParamSets.BLOCK
)), registries));
generator.addProvider(true,
new ExampleRecipeProvider.Runner(output, registries));
generator.addProvider(true,
new ExampleBlockTagsProvider(output, registries));
generator.addProvider(true,
new ExampleItemTagsProvider(output, registries));
generator.addProvider(true,
new AdvancementProvider(output, registries,
List.of(new ExampleAdvancementProvider())));
generator.addProvider(true,
new DatapackBuiltinEntriesProvider(output, registries,
new RegistrySetBuilder()
.add(Registries.ENCHANTMENT, ExampleEnchantmentBootstrap::bootstrap),
Set.of(Constants.MOD_ID)));
}

If you add more datapack registry types in future (biomes, damage types, configured features), chain additional .add(registry, bootstrap) calls on the same RegistrySetBuilder.

Language Keys

The component passed to the Enchantment constructor is the display name. Add the translation key to ExampleLanguageProvider:

java
// In ExampleLanguageProvider.addTranslations():
add("enchantment.examplemod.example_enchantment", "Example Enchantment");

Running Datagen

Run the NeoForge Data configuration. Check common/src/generated/resources/data/examplemod/enchantment/ for the new file:

json
// data/examplemod/enchantment/example_enchantment.json (excerpt)
{
"anvil_cost": 2,
"description": {
"translate": "enchantment.examplemod.example_enchantment"
},
"effects": {},
"exclusive_set": [],
"max_cost": { "base": 20, "per_level_above_first": 10 },
"max_level": 3,
"min_cost": { "base": 1, "per_level_above_first": 10 },
"primary_items": "#minecraft:sword_enchantable",
"slots": ["mainhand"],
"supported_items": "#minecraft:sword_enchantable",
"weight": 10
}

Testing In-Game

Launch the client, open a creative inventory, and use an enchanted book to verify the enchantment appears. You can also apply it directly with a command:

shell
/enchant @s examplemod:example_enchantment 1

The enchantment will show its name and level but have no active effect yet, since DataComponentMap.EMPTY was passed for the effects. Adding gameplay behaviour via EnchantmentEffectComponents is covered in the custom enchantments tutorial later in the series.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Custom Food Items (MultiLoader 26.1+)

Create an edible item using FoodProperties.Builder, configure nutrition, saturation, and optional status effects on eating, then wire up the model and creative tab entry through datagen.

Continue →