Wiki 9Minecraft Just Enough Items Wiki
Just Enough Items WikiDocumentation › Custom Ingredient Types %5Bminecraft 1.21.11 To 26.2%5D

Custom Ingredient Types %5Bminecraft 1.21.11 To 26.2%5D

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, Ingredients and Visibility
  • Minecraft 1.18.2, 1.19.2, and 1.20.1: Adding/Hiding Items, Item Subtypes, Non-Item Ingredients
  • Minecraft 1.16.5: Adding/Hiding Items, Item Subtypes, Non-Item Ingredients
  • Minecraft 1.12.2: Non-Item Ingredients, Adding Items, Item Subtypes

Custom Ingredient Types

Use a custom ingredient type only when item stacks and platform fluid stacks cannot represent what your recipe uses. Examples include spells, gases, research entries, species, or virtual resources.

If your data can be represented as an ItemStack with components, use item subtypes instead.

Register The Type

Register custom ingredient types in registerIngredients.

@Override
public void registerIngredients(IModIngredientRegistration registration) {
  registration.register(
    SpellIngredient.TYPE,
    SpellRegistry.getKnownSpells(),
    new SpellIngredientHelper(),
    new SpellIngredientRenderer(),
    SpellIngredient.CODEC
  );
}

The registration needs:

  • An IIngredientType<T>.
  • Every ingredient JEI should display initially.
  • An IIngredientHelper<T>.
  • An IIngredientRenderer<T>.
  • A Codec<T> for saving and loading bookmarks and JEI data.

Ingredient Type

The type identifies the Java class and the persistent ingredient type ID.

public record SpellIngredient(String id, Component name) {
  public static final IIngredientType<SpellIngredient> TYPE = new IIngredientType<>() {
    @Override
    public String getUid() {
      return "examplemod:spell";
    }

    @Override
    public Class<? extends SpellIngredient> getIngredientClass() {
      return SpellIngredient.class;
    }
  };

  public static final Codec<SpellIngredient> CODEC = RecordCodecBuilder.create(instance ->
    instance.group(
      Codec.STRING.fieldOf("id").forGetter(SpellIngredient::id)
    ).apply(instance, SpellRegistry::getSpell)
  );
}

Use a stable UID. Changing it breaks saved JEI data for that ingredient type.

Ingredient Helper

The helper provides search, identity, grouping, copying, and error information.

public final class SpellIngredientHelper implements IIngredientHelper<SpellIngredient> {
  @Override
  public IIngredientType<SpellIngredient> getIngredientType() {
    return SpellIngredient.TYPE;
  }

  @Override
  public String getDisplayName(SpellIngredient ingredient) {
    return ingredient.name().getString();
  }

  @Override
  public Object getUid(SpellIngredient ingredient, UidContext context) {
    return ingredient.id();
  }

  @Override
  public Identifier getIdentifier(SpellIngredient ingredient) {
    return Identifier.fromNamespaceAndPath("examplemod", ingredient.id());
  }

  @Override
  public SpellIngredient copyIngredient(SpellIngredient ingredient) {
    return ingredient;
  }

  @Override
  public String getErrorInfo(SpellIngredient ingredient) {
    return String.valueOf(ingredient);
  }
}

The UID returned from getUid must have stable equals and hashCode. Do not return mutable objects.

Ingredient Renderer

The renderer draws the ingredient in JEI's ingredient list and recipe slots.

public final class SpellIngredientRenderer implements IIngredientRenderer<SpellIngredient> {
  @Override
  public void render(GuiGraphicsExtractor guiGraphics, SpellIngredient ingredient) {
    SpellIconRenderer.draw(guiGraphics, ingredient, 0, 0);
  }

  @Override
  public List<Component> getTooltip(SpellIngredient ingredient, TooltipFlag tooltipFlag) {
    return List.of(
      ingredient.name(),
      Component.translatable("jei.examplemod.spell_id", ingredient.id())
    );
  }
}

The default size is 16 by 16 pixels. Override getWidth and getHeight only when every slot using this renderer is prepared for the larger size.

Using Custom Ingredients In Recipes

Add custom ingredients to recipe layouts with your type.

builder.addInputSlot(1, 10)
  .setStandardSlotBackground()
  .add(SpellIngredient.TYPE, recipe.spell());

Add aliases and info pages by passing the custom type.

registration.addAlias(SpellIngredient.TYPE, ModSpells.FROST, "ice");
registration.addIngredientInfo(
  ModSpells.FROST,
  SpellIngredient.TYPE,
  Component.translatable("jei.examplemod.info.frost_spell")
);

Checklist

  • Make the codec support normalized ingredients that can be saved and loaded.
  • Keep copyIngredient safe. JEI may copy ingredients to avoid mutation.
  • Implement isValidIngredient if your type has empty or placeholder values.
  • Implement getTagStream if players should be able to search this ingredient by tags.
  • Use registerExtraIngredients or runtime ingredient APIs only after the type itself has been registered.