User Tools

Site Tools


← Go back to the homepage

Fabric Tutorials



These pages are essential must-reads when modding with Fabric, and modding Minecraft in general, if you are new to modding, it is recommended you read the following.


Blocks and Block Entities

Data Generation

World Generation


These pages will guide you through Mojang's Brigadier library which allows you to create commands with complex arguments and actions.


These pages will guide you through using the many events included in Fabric API, and how to create your own events for you or other mods to use.



Mixins & ASM

These pages will guide you through the usage of SpongePowered's Mixin library, which is a highly complex topic. We recommend you read these pages thoroughly.



Contribute to Fabric


Creating a Custom Recipe Type

In this page, we will create our own custom recipe type, including the shapeless version! We won't use any vanilla Minecraft classes, it will be a fully custom recipe type.

You can find and learn how to make other recipe types. Creating a custom cooking recipe type

Adding the Block

We will need to create a block to open the recipe gui. Some of the classes will be created later.
public class TestRecipeBlock extends Block {
	private static final Text TITLE = new TranslatableText("container.test_crafting");
	public TestRecipeBlock(Settings settings) {
	public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
		if(world.isClient) {
			return ActionResult.SUCCESS;
		} else {
			player.openHandledScreen(state.createScreenHandlerFactory(world, pos));
			return ActionResult.CONSUME;
	public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
		return new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> {
			return new TestCraftingScreenHandler(i, playerInventory, ScreenHandlerContext.create(world, pos));
		}, TITLE);

Now we need to register the Block and BlockItem.
public class ExampleCustomRecipeMod implements ModInitializer {
	public static final TestRecipeBlock TEST_RECIPE_BLOCK = new TestRecipeBlock(FabricBlockSettings.of(Material.METAL));
	public void onInitialize() {  
    		// Resuming later...

Creating the Recipe Class

Now we will do the most fun part of this page, creating the recipe class.

Creating the Shaped Recipe Class

Shaped recipes are a recipe type that requires an item to be in that specific slot or pattern. It is mostly used on grid recipes such as Crafting Recipe.

The Recipe interface expects an Inventory class as a type parameter. Any inventory that contains the ingredients for the recipe would work. But we will be using CraftingInventory.
public class TestRecipe implements Recipe<CraftingInventory> {
	//You can add as much inputs as you want here.
	//It is important to always use Ingredient, so you can support tags.
	private final Ingredient inputA;
	private final Ingredient inputB;
	private final ItemStack result;
	private final Identifier id;
	public TestRecipe(Identifier id, ItemStack result, Ingredient inputA, Ingredient inputB) { = id;
		this.inputA = inputA;
		this.inputB = inputB;
		this.result = result;
	public Ingredient getInputA() {
		return this.inputA;
	public Ingredient getInputB() {
		return this.inputB;
	public ItemStack getOutput() {
		return this.result;
	public Identifier getId() {

We would need to care about craft, because it is needed to craft the result. In craft, it is a good idea to return this.getOutput().copy(). fits should return true.
public class TestRecipe implements Recipe<CraftingInventory> {
	public ItemStack craft(CraftingInventory inv) {
		return this.getOutput().copy();
	public boolean fits(int width, int height) {
		return true;

In matches, we need to say if a given inventory satisfies a recipe's input. We will return true if the first and second slot matches first and second input.
public class TestRecipe implements Recipe<CraftingInventory> {
	public boolean matches(CraftingInventory inv, World world) {
		if(inv.size(0) < 2) return false;
		return inputA.test(inventory.getStack(0)) && inputB.test(inventory.getStack(1));

Now we need the Type of the recipe. All you have to do is to create an instance of a class that extends RecipeType<TestRecipe>.
public class TestRecipe implements Recipe<CraftingInventory> {
	public static class Type implements RecipeType<TestRecipe> {
		private Type() {}
		public static final Type INSTANCE = new Type();
		public static final String ID = "test_recipe";
	pubilc RecipeType<?> getType() {
		return Type.INSTANCE;

What's still missing is the RecipeSerializer, for now try to figure it out using the SOURCE link below.

SOURCE: Defining Custom Crafting Recipes

tutorial/recipe_type.txt · Last modified: 2021/12/01 11:46 by ayutac