MultiLoader 26.1+ · Part 7

Data Generation: Crafting Recipes (MultiLoader 26.1+)

INTERMEDIATE MULTILOADER 26.1-26.1.2 20 min read · Jun 12, 2026
MultiLoader 26.1+ · 17 parts
1 Getting Started with MultiLoader 26.1+ 2 Setting Up RegistrationUtils (MultiLoader 26.1+) 3 Creating Items (MultiLoader 26.1+) 4 Creating Blocks (MultiLoader 26.1+) 5 Data Generation: Block & Item Models (MultiLoader 26.1+) 6 Data Generation: Block Loot Tables (MultiLoader 26.1+) 7 Data Generation: Crafting Recipes (MultiLoader 26.1+) 8 Data Generation: Block & Item Tags (MultiLoader 26.1+) 9 Data Generation: Language Files (MultiLoader 26.1+) 10 Data Generation: Advancements (MultiLoader 26.1+) 11 Data Generation: Sound Definitions (MultiLoader 26.1+) 12 Data Generation: Particle Descriptions (MultiLoader 26.1+) 13 Data Generation: Enchantments (MultiLoader 26.1+) 14 Custom Food Items (MultiLoader 26.1+) 15 Custom Tools (MultiLoader 26.1+) 16 Custom Armour (MultiLoader 26.1+) 17 Block Entities (MultiLoader 26.1+)

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 26.1, RecipeProvider uses a two-class pattern. The provider itself receives the fully resolved HolderLookup.Provider and a RecipeOutput through its constructor. A static inner Runner class handles the asynchronous lookup resolution and is what you register with the data generator.

In your NeoForge data package, create ExampleRecipeProvider:

java
public class ExampleRecipeProvider extends RecipeProvider {
protected ExampleRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) {
super(registries, output);
}
@Override
protected void buildRecipes() {
// recipe definitions go here — use the inherited output field to save each recipe
}
public static class Runner extends RecipeProvider.Runner {
public Runner(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider) {
super(output, lookupProvider);
}
@Override
protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) {
return new ExampleRecipeProvider(registries, output);
}
@Override
public String getName() {
return "ExampleMod Recipes";
}
}
}

All recipe definitions go inside buildRecipes(). Unlike earlier NeoForge versions, this method takes no parameters: the RecipeOutput is stored as a protected field (output) by the parent constructor, so you reference it directly when calling save on each builder. The Runner is what you register with the generator; it resolves the async lookup and then delegates to createRecipeProvider to construct the actual provider instance.

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, Identifier.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 Identifier 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, Identifier.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, Identifier.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 Runner alongside the existing loot provider:

java
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
CompletableFuture<HolderLookup.Provider> registries = event.getLookupProvider();
generator.addProvider(true,
new LootTableProvider(output, Set.of(),
List.of(new LootTableProvider.SubProviderEntry(
ExampleBlockLootTableProvider::new,
LootContextParamSets.BLOCK
)), registries));
generator.addProvider(true,
new ExampleRecipeProvider.Runner(output, registries));
}

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 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 26.1+)

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 →