Custom Machine Recipes [Minecraft 1.21.11 to 26.2] - mezz/JustEnoughItems GitHub Wiki
[!NOTE] Version note: This page covers Minecraft 1.21.11, 26.1.2, and 26.2.
Related pages for other README-supported versions:
- Minecraft 1.21 and 1.21.1: Getting Started, Creating Your Plugin, Recipes Overview, Recipe Categories
- Minecraft 1.18.2, 1.19.2, and 1.20.1: Recipes Overview, Recipe Categories, Essential Extras
- Minecraft 1.16.5: Recipes Overview, Recipe Categories, Essential Extras
- Minecraft 1.12.2: Recipes Overview, Recipe Categories, Recipe Wrappers, Recipe Handlers
Custom Machine Recipes
Use a custom recipe category when your mod has a recipe type that does not fit one of JEI's built-in categories. The normal flow is:
- Create one shared
IRecipeType<T>. - Register an
IRecipeCategory<T>inregisterCategories. - Add recipe instances in
registerRecipes. - Register the machine block or item as a crafting station in
registerRecipeCatalysts.
public static final IRecipeType<CrusherRecipe> CRUSHING =
IRecipeType.create("examplemod", "crushing", CrusherRecipe.class);
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
IGuiHelper guiHelper = registration.getJeiHelpers().getGuiHelper();
registration.addRecipeCategories(new CrusherRecipeCategory(guiHelper));
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
registration.addRecipes(CRUSHING, ClientRecipeCache.getCrusherRecipes());
}
@Override
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) {
registration.addCraftingStation(CRUSHING, ModBlocks.CRUSHER);
}
Keep the IRecipeType in one place and reuse it everywhere. JEI uses it to connect categories, recipe lists, crafting stations, click areas, transfer handlers, runtime updates, and recipe lookups.
Recipe Category
AbstractRecipeCategory handles the shared type, title, icon, width, and height. Your category still needs to fill the recipe layout.
public final class CrusherRecipeCategory extends AbstractRecipeCategory<CrusherRecipe> {
public CrusherRecipeCategory(IGuiHelper guiHelper) {
super(
ExampleJeiPlugin.CRUSHING,
Component.translatable("jei.examplemod.crushing"),
guiHelper.createDrawableItemLike(ModBlocks.CRUSHER),
116,
38
);
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, CrusherRecipe recipe, IFocusGroup focuses) {
builder.addInputSlot(1, 10)
.setStandardSlotBackground()
.add(recipe.input());
builder.addOutputSlot(95, 10)
.setOutputSlotBackground()
.add(recipe.result());
builder.addInvisibleIngredients(RecipeIngredientRole.CRAFTING_STATION)
.add(ModBlocks.CRUSHER);
}
}
Use slot roles for recipe lookup behavior:
INPUTfor consumed ingredients.OUTPUTfor results.CRAFTING_STATIONfor required, non-consumed machines or tools.RENDER_ONLYfor visuals that should not affect recipe lookup.
Use addInvisibleIngredients when an ingredient matters for lookup but should not be drawn in the layout.
Layout Details
For normal slots, use setStandardSlotBackground. For a result slot, use setOutputSlotBackground.
For fluids, set the tank size on the slot:
builder.addInputSlot(20, 2)
.setFluidRenderer(1000, true, 16, 34)
.add(recipe.fluid(), recipe.fluidAmount());
For recipes where input and output lists rotate together, link the slots:
IRecipeSlotBuilder input = builder.addInputSlot(1, 10)
.setStandardSlotBackground()
.addItemStacks(recipe.inputs());
IRecipeSlotBuilder output = builder.addOutputSlot(95, 10)
.setOutputSlotBackground()
.addItemStacks(recipe.outputs());
builder.createFocusLink(input, output);
Common Mistakes
- Do not create a new
IRecipeTypeinstance in each registration method. Reuse one static value. - Do not put all visual data in
draw. Ingredients should be added throughsetRecipeso JEI can search and focus them. - Do not use deprecated catalyst methods on these versions. Use
addCraftingStation. - Do not rely on client-only recipe discovery unless the data really is client-only. JEI recipe lists should match the player's recipe data.