MultiLoader 1.21+ · Part 7

Data Generation: Crafting Recipes (MultiLoader 1.21+)

INTERMEDIATE MULTILOADER 1.21-1.21.1 20 min read · Jun 22, 2026
MultiLoader 1.21+ · 28 parts
1 Getting Started with MultiLoader 1.21+ 2 Setting Up RegistrationUtils 3 Creating Items (MultiLoader 1.21+) 4 Creating Blocks (MultiLoader 1.21+) 5 Data Generation: Block & Item Models (MultiLoader 1.21+) 6 Data Generation: Block Loot Tables (MultiLoader 1.21+) 7 Data Generation: Crafting Recipes (MultiLoader 1.21+) 8 Data Generation: Block & Item Tags (MultiLoader 1.21+) 9 Custom Food Items (MultiLoader 1.21+) 10 Custom Tools (MultiLoader 1.21+) 11 Custom Armour (MultiLoader 1.21+) 12 Block Entities (MultiLoader 1.21+) 13 Config Files (MultiLoader 1.21+) 14 Custom Sounds (MultiLoader 1.21+) 15 Events and Listeners (MultiLoader 1.21+) 16 Networking and Custom Packets (MultiLoader 1.21+) 17 Data Generation: Advancements (MultiLoader 1.21+) 18 Data Generation: Language Files (MultiLoader 1.21+) 19 Custom Entities (MultiLoader 1.21+) 20 Ore Generation (MultiLoader 1.21+) 21 Introduction to Mixins (MultiLoader 1.21+) 22 Custom Particles (MultiLoader 1.21+) 23 Menus & Screens (MultiLoader 1.21+) 24 Key Bindings (MultiLoader 1.21+) 25 Custom Potion Effects (MultiLoader 1.21+) 26 Custom Enchantments (MultiLoader 1.21+) 27 Custom Commands (MultiLoader 1.21+) 28 Custom Biomes (MultiLoader 1.21+)

With models and loot tables generating automatically, the next step is crafting recipes. NeoForge's datagen system handles these through a RecipeProvider, which lets you define shaped, shapeless, smelting and blasting recipes entirely in Java and outputs the corresponding JSON files into the common project alongside everything else.

NOTE
Complete the Data Generation: Block Loot Tables tutorial before continuing. We add a new provider to the existing gatherData method.

Recipe Provider

In your NeoForge data package, create a class called ExampleRecipeProvider extending RecipeProvider. Unlike the loot and model providers, RecipeProvider receives the lookup registries as a CompletableFuture rather than a resolved instance:

java
public class ExampleRecipeProvider extends RecipeProvider {
public ExampleRecipeProvider(PackOutput output,
CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries);
}
@Override
protected void buildRecipes(RecipeOutput output) {
// recipe definitions go here
}
}

All recipe definitions go inside buildRecipes. The RecipeOutput parameter collects each recipe and its associated advancement unlock condition and writes both to JSON during datagen.

Shaped Recipes

A shaped recipe requires ingredients to be placed in a specific pattern in the crafting grid. Use ShapedRecipeBuilder to define the pattern, map each character to an ingredient, then provide an advancement criterion that unlocks the recipe in the recipe book. Here we craft four Iron Sticks from two iron ingots placed vertically:

java
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ItemRegistry.IRON_STICK.get(), 4)
.pattern("I")
.pattern("I")
.define('I', Items.IRON_INGOT)
.unlockedBy("has_iron_ingot", has(Items.IRON_INGOT))
.save(output);

Each row of pattern corresponds to one row in the crafting grid. Characters can be anything except a space, which always means an empty slot. The define call maps the character to an ingredient, which can be a single item, a tag, or a compound ingredient.

The file generated for the above recipe will be saved at data/examplemod/recipe/iron_stick.json:

json
{
"type": "minecraft:crafting_shaped",
"pattern": ["I", "I"],
"key": {
"I": { "item": "minecraft:iron_ingot" }
},
"result": {
"count": 4,
"id": "examplemod:iron_stick"
}
}
TIP
To use a tag as an ingredient rather than a specific item, pass a TagKey<Item> to define instead of an ItemLike. This lets players substitute any item in the tag, for example using any plank variant where a specific plank would otherwise be required.

Shapeless Recipes

A shapeless recipe accepts its ingredients in any arrangement. Use ShapelessRecipeBuilder and call requires once per ingredient slot. Here we convert dirt and an Iron Stick into a New Dirt block as a simple demonstration:

java
ShapelessRecipeBuilder.shapeless(RecipeCategory.BUILDING_BLOCKS, BlockRegistry.NEW_DIRT.get())
.requires(Items.DIRT)
.requires(ItemRegistry.IRON_STICK.get())
.unlockedBy("has_iron_stick", has(ItemRegistry.IRON_STICK.get()))
.save(output, ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "new_dirt_from_stick"));

When the output item already has a shaped recipe saved under its registry name, pass an explicit ResourceLocation as the second argument to save to give this recipe a unique file name and avoid a clash.

json
{
"type": "minecraft:crafting_shapeless",
"ingredients": [
{ "item": "minecraft:dirt" },
{ "item": "examplemod:iron_stick" }
],
"result": {
"count": 1,
"id": "examplemod:new_dirt"
}
}

Smelting and Blasting

Furnace recipes use SimpleCookingRecipeBuilder. The static factory methods smelting and blasting select the recipe type automatically. Here we smelt New Dirt into Coarse Dirt at 0.1 experience per operation:

java
// Smelting (furnace only)
SimpleCookingRecipeBuilder.smelting(
Ingredient.of(BlockRegistry.NEW_DIRT.get()),
RecipeCategory.BUILDING_BLOCKS,
Items.COARSE_DIRT,
0.1f,
200)
.unlockedBy("has_new_dirt", has(BlockRegistry.NEW_DIRT.get()))
.save(output, ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "coarse_dirt_from_smelting"));
// Blasting (blast furnace, twice as fast)
SimpleCookingRecipeBuilder.blasting(
Ingredient.of(BlockRegistry.NEW_DIRT.get()),
RecipeCategory.BUILDING_BLOCKS,
Items.COARSE_DIRT,
0.1f,
100)
.unlockedBy("has_new_dirt", has(BlockRegistry.NEW_DIRT.get()))
.save(output, ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "coarse_dirt_from_blasting"));

The four arguments after the ingredient are: the output item, XP reward, and cooking time in ticks (20 ticks per second). Blasting recipes conventionally use half the cook time of the equivalent smelting recipe.

TIP
Other cooking recipe types follow the same pattern. Use SimpleCookingRecipeBuilder.smoking for smoker recipes and SimpleCookingRecipeBuilder.campfireCooking for campfire recipes.

Registering the Provider

Open the gatherData method in your NeoForge mod class and register the recipe provider alongside the existing ones. The lookup provider is passed as a CompletableFuture from the event:

java
public static void gatherData(GatherDataEvent event) {
try {
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
CompletableFuture<HolderLookup.Provider> registries = event.getLookupProvider();
generator.addProvider(true, new ExampleItemModelProvider(output, existingFileHelper));
generator.addProvider(true, new ExampleBlockStateProvider(output, Constants.MOD_ID, existingFileHelper));
generator.addProvider(true,
new LootTableProvider(output, Set.of(),
List.of(new LootTableProvider.SubProviderEntry(
ExampleBlockLootTableProvider::new,
LootContextParamSets.BLOCK
)), registries));
generator.addProvider(true,
new ExampleRecipeProvider(output, registries));
} catch (RuntimeException e) {
Constants.LOG.error("Failed to generate data", e);
}
}

Running Datagen

Run the NeoForge Data configuration. After it completes, check common/src/generated/resources/data/examplemod/recipe/. You should see one file per recipe you registered. Launch the client and confirm:

  • The shaped recipe for Iron Stick appears in the crafting table recipe book once you pick up an iron ingot.
  • The shapeless New Dirt recipe works with the ingredients in any slot arrangement.
  • New Dirt can be smelted in a furnace and blasted in a blast furnace, yielding Coarse Dirt.
TIP
The recipe book in Minecraft shows recipes that the player has "unlocked" via the unlockedBy criterion. In creative mode all recipes are visible regardless, so switch to survival or use /recipe give @s * when testing unlock behaviour.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Data Generation: Block & Item Tags (MultiLoader 1.21+)

Generate block and item tag files with BlockTagsProvider and ItemTagsProvider, add your custom block to vanilla tool-requirement tags, and create a mod-scoped item tag for use as a recipe ingredient.

Continue →