MC Toolkit

Guides / Commands & Data

Custom enchantments without mods: the data-driven enchantment format

Enchantments became datapack JSON, so a custom one needs no mod. Here is the file shape, the effect components that actually do something, and how to make it obtainable.

Enchantments used to be hardcoded. They are now datapack data, which means a genuinely custom enchantment — new name, new effect, new cost curve — is a JSON file.

Where it lives

data/<namespace>/enchantment/lightning_strike.json

Singular enchantment/, like every other datapack category since the rename.

The minimum file

{
  "description": { "translate": "enchantment.my_pack.lightning_strike" },
  "supported_items": "#minecraft:enchantable/melee_weapon",
  "weight": 2,
  "max_level": 3,
  "min_cost": { "base": 10, "per_level_above_first": 10 },
  "max_cost": { "base": 50, "per_level_above_first": 10 },
  "anvil_cost": 4,
  "slots": ["mainhand"]
}

That much gives a real enchantment that loads and shows on the item. It does nothing yet — behaviour is separate — and the table will not offer it until it is tagged (below).

  • weight is rarity at the enchanting table. Vanilla uses 10 for common, 1 for very rare. It is relative, not a percentage.
  • slots decides where the enchantment is active: mainhand, offhand, armor, head, and so on. It is required: omit it and the file fails to load.
  • supported_items takes an item tag or a list. #minecraft:enchantable/* tags already exist for the sensible groups — melee_weapon, sharp_weapon, mining, armor and so on. There is no enchantable/sword; a tag that does not exist fails the file.

Making it do something

effects attaches behaviour:

"effects": {
  "minecraft:post_attack": [{
    "enchanted": "attacker",
    "affected": "victim",
    "effect": { "type": "minecraft:run_function", "function": "my_pack:strike" },
    "requirements": { "condition": "minecraft:random_chance", "chance": 0.25 }
  }]
}

post_attack needs to know whose enchantment it is (enchanted) and who the effect lands on (affected) — both are required. run_function is the escape hatch — anything a function can do, the enchantment can do. The built-in effect types cover damage, knockback, durability, item drops and attributes without needing a function at all, and they are cheaper.

requirements takes any predicate, so gating on the target's type, the weather, or a random chance is one block of JSON.

Naming it

description is a text component, so a hardcoded name works:

"description": { "text": "Lightning Strike", "color": "gold" }

but a translate key plus a language file in a resource pack is what makes it read correctly for players in other languages — the same reason this site takes game terms from Mojang's own translations.

Making it obtainable

Three routes, and you usually want more than one:

  • Enchanting table — once weight and the cost curve are set and the enchantment is in #minecraft:in_enchanting_table.
  • Loot tables — enchant_randomly with your enchantment listed, or enchant_with_levels if it should compete on cost.
  • Villager trades — an enchanted book with your enchantment in the trade's item.

Adding it to #minecraft:in_enchanting_table is what makes the table offer it at all; the tag is easy to forget and produces an enchantment that exists but never appears.

Build the enchantment JSON and its armor-trim counterpart, with the tags and slots set, in the Custom Enchantment & Trim Builder.

Custom Enchantment & Trim Builder →

More guides

Browse all →