Mob effects (status effects) are the timed buffs and debuffs applied by potions, beacons, and enchantments. In 1.21.1 the effect system is split into two separate registries: the MobEffect registry holds the effect type and its logic, while the Potion registry holds named combinations of effects and durations that can be applied by potion items. This tutorial creates a custom healing effect, a potion that applies it, and registers brewing recipes on both loaders.
The Effect Class
Create an ExampleEffect class in an effect package inside your common project. Extend MobEffect and override the two tick methods:
MobEffectCategory controls the colour of the effect icon border and the potion colour:
- BENEFICIAL: Blue icon border. Used for Speed, Regeneration, Strength.
- HARMFUL: Red icon border. Used for Poison, Weakness, Blindness.
- NEUTRAL: Grey icon border. Used for Night Vision, Water Breathing.
shouldApplyEffectTickThisTick is called every game tick while the effect is active. Returning false skips the applyEffectTick call for that tick. This is important for performance: an effect that heals every tick would apply 20 times per second on every living entity carrying it. Spacing applications out with the bit-shift trick ( 40 >> amplifier) halves the interval for each amplifier level, matching the vanilla Regeneration pattern.
false from applyEffectTick removes the effect from the entity immediately. This is useful for one-shot effects that should self-remove after applying (for example, an instant-heal effect). For ongoing effects that simply tick less often, always return true.Effect Registry
Create an EffectRegistry class in your common registry package:
Call EffectRegistry.init() from CommonClass.init():
Effects must be registered before potions because the MobEffectInstance constructor in 1.21.1 takes a Holder<MobEffect>, which is resolved from the registry at registration time.
common/src/main/resources/assets/examplemod/textures/mob_effect, with the PNG file of the texture having a size of 18x18. Since we called our effect in the registry example_effect, that is the name of our PNG file.Attribute Modifiers
Some effects permanently modify an entity's attributes while active (Speed increases movement speed, Strength increases attack damage). Add attribute modifiers in the ExampleEffect constructor by calling addAttributeModifier. Each modifier needs a stable ResourceLocation ID so it can be correctly removed when the effect expires:
Operation.ADD_VALUE adds a flat amount per amplifier level. Operation.ADD_MULTIPLIED_BASE multiplies the base attribute value before other modifiers, and Operation.ADD_MULTIPLIED_TOTAL multiplies the final value after all other modifiers. The vanilla Speed effect uses ADD_MULTIPLIED_BASE so higher levels multiply more aggressively. Use a unique ResourceLocation ID for each modifier to avoid clashes with vanilla or other mods.
Custom Potion
A Potion is a named collection of MobEffectInstance objects. Create one that applies your effect for 45 seconds (900 ticks) at level I. In 1.21.1, MobEffectInstance takes a Holder<MobEffect> rather than the raw effect, which is obtained from the RegistryObject via asHolder():
Potion duration is always specified in ticks (20 ticks per second). The amplifier is zero-indexed: amplifier 0 displays as "Level I", amplifier 1 displays as "Level II", and so on. You can pass multiple MobEffectInstance arguments to a single Potion to create a potion that applies several effects simultaneously, like vanilla's "Turtle Master" potion.
Potion Registry
Create a PotionRegistry class in your common registry package. Register both a normal-strength and a strong variant:
Call PotionRegistry.init() from CommonClass.init() after EffectRegistry.init(), since the potion suppliers reference the effect holder.
"example" generates the translation key item.minecraft.potion.effect.example for the normal potion item and item.minecraft.splash_potion.effect.example for the splash variant. Add entries for all four item types (potion, splash_potion, lingering_potion, tipped_arrow) in your lang file.Brewing Recipes
Brewing recipes associate an input potion, a brewing ingredient, and an output potion. In 1.21.1 the brewing recipe registry is loader-specific, so the registration must be split between the two subprojects.
On NeoForge, register using RegisterBrewingRecipesEventon the game bus. The event provides a PotionBrewing.Builder that accepts ingredient-to-potion and potion-to-potion recipes. Let's do this in our existing ExampleEvents class:
On Fabric, use FabricBrewingRecipeRegistryBuilder from the Fabric API module fabric-brewing-recipe-registry-api-v1:
addMix is compared by item identity, not by tag. If you want to accept any item in a tag (for example, any type of melon), create a custom Ingredient and use the overload of addMix that accepts an Ingredient instead of an Item.Lang Entries
Add translation entries for the effect name and all four potion item types into the lang generator. All four item keys (potion, splash, lingering, tipped arrow) share the same suffix but differ in prefix:
The effect translation key uses your mod ID as the namespace: effect.examplemod.example_effect. The potion item keys use minecraft as the namespace because those are vanilla items; only the suffix after effect. is your registry name.
Testing
Run /effect give @s examplemod:example_effect 60 0 to apply the effect for 60 seconds at level I. Open the inventory and confirm the effect icon appears with a blue border and the name "Vitality". Monitor your health bar to confirm the healing tick fires approximately every two seconds.
To test brewing, switch to survival mode, place a brewing stand, add Blaze Powder as fuel, put Awkward Potions in the three bottom slots, and add a Glistering Melon Slice to the top slot. After brewing completes, the potions should change to "Potion of Vitality". Brew a second time with Glowstone Dust to confirm the strong variant is produced.
Use /give @s potion[minecraft:potion_contents="examplemod:example"] in a creative world to skip the brewing step during testing.
You can find the source for this tutorial here:
View Source on GitHubCustom Enchantments (MultiLoader 1.21+)
Understand the 1.21 data-driven enchantment overhaul, write an enchantment JSON using built-in post_attack and apply_mob_effect components, generate it with DatapackBuiltinEntriesProvider, and optionally implement a custom EnchantmentEntityEffect codec for novel behaviour.
Continue →