How to Create Your Own Datapack, Beginner Guide
Learn how to create a Minecraft datapack from scratch: folder structure, pack.mcmeta, custom recipes, advancements, loot tables, mcfunction files, tick.json, load.json, and debugging.
Building your own datapack is one of the most rewarding things you can do in Minecraft without touching a single line of Java or installing a modding framework. Everything is JSON and plain-text function files. You can create custom recipes, modify loot tables, add advancements with rewards, run scheduled commands, and tweak game mechanics, all with a text editor and a basic understanding of Minecraft's data format. This guide starts from zero and walks through every step.
Setting up the folder structure
A datapack is just a folder with a specific layout. Create a new folder inside your world's datapacks/ directory. Call it something descriptive, like my_server_tweaks. Inside that folder, you need two things at the root level:
my_server_tweaks/
pack.mcmeta
data/
my_namespace/
recipes/
advancements/
loot_tables/
functions/
tags/
pack.mcmeta
This file identifies your folder as a datapack. Create it with this content:
{
"pack": {
"pack_format": 48,
"description": "Custom tweaks for our server"
}
}
Change pack_format to match your Minecraft version. For 1.21.x, use 48 or higher depending on the sub-version. The description appears in-game when you run /datapack list.
Choosing a namespace
The namespace is the folder name under data/ and acts like a unique identifier for your content. Use lowercase letters, digits, and underscores only. Avoid using minecraft as your namespace unless you intentionally want to override vanilla behavior (like modifying a vanilla loot table). A good convention is your server name: data/myserver/.
Creating a custom recipe
Recipes are the easiest starting point. Let's make a recipe that crafts four chains from two iron ingots and two iron nuggets, because the default recipe gives too few chains for the cost.
Create the file at data/my_namespace/recipes/better_chains.json:
{
"type": "minecraft:crafting_shaped",
"pattern": [
"N",
"I",
"N"
],
"key": {
"N": { "item": "minecraft:iron_nugget" },
"I": { "item": "minecraft:iron_ingot" }
},
"result": {
"id": "minecraft:chain",
"count": 4
}
}
Save the file, run /reload in-game, and check the recipe book. Your new recipe should appear alongside the vanilla one. Players will see both and can use whichever they prefer.
Creating an advancement
Advancements are the notification popups that appear when a player achieves something. You can create custom ones with rewards like experience, items, or function execution.
Create data/my_namespace/advancements/mine_deepslate_diamond.json:
{
"display": {
"icon": { "id": "minecraft:deepslate_diamond_ore" },
"title": "Deep Treasure",
"description": "Mine a deepslate diamond ore block",
"frame": "task",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"mine_deepslate_diamond": {
"trigger": "minecraft:player_interacted_with_block",
"conditions": {
"location": {
"block": {
"blocks": ["minecraft:deepslate_diamond_ore"]
}
}
}
}
},
"rewards": {
"experience": 50
}
}
The trigger field tells the game what event to watch for. Minecraft has dozens of built-in triggers, minecraft:inventory_changed, minecraft:enter_block, minecraft:killed_by_crossbow, and many more. Check the Minecraft Wiki for the full list and their condition formats.
Modifying a loot table
Loot tables control what drops from mobs, chests, fishing, and block breaking. To modify a vanilla loot table, you use the minecraft namespace and mirror the vanilla file path.
For example, to make Creepers always drop a music disc, create data/minecraft/loot_tables/entities/creeper.json. The trick here is that your file completely replaces the vanilla loot table for creepers. You need to include the original drops (gunpowder) plus your addition:
{
"type": "minecraft:entity",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:gunpowder",
"functions": [
{
"function": "minecraft:set_count",
"count": { "min": 0, "max": 2 }
},
{
"function": "minecraft:looting_enchant",
"count": { "min": 0, "max": 1 }
}
]
}
]
},
{
"rolls": 1,
"bonus_rolls": 0,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:music_disc_cat",
"conditions": [
{
"condition": "minecraft:random_chance",
"chance": 0.05
}
]
}
]
}
]
}
This gives creepers a 5% chance to drop the "Cat" music disc on top of their normal gunpowder. Be careful with loot table overrides, if two datapacks modify the same loot table, the last-loaded one wins and the other is silently ignored.
Function files (.mcfunction)
Functions are lists of commands stored in text files. They run sequentially, top to bottom, as if an operator typed each line into chat. Create a function at data/my_namespace/functions/welcome.mcfunction:
# Welcome function, runs when called
title @s title {"text":"Welcome!","color":"gold"}
title @s subtitle {"text":"Enjoy your stay","color":"gray"}
playsound minecraft:entity.player.levelup master @s ~ ~ ~ 1 1
give @s minecraft:bread 16
Run it with /function my_namespace:welcome. The @s selector refers to whoever executed the function. Lines starting with # are comments and are ignored by the server.
tick.json and load.json
These two special tag files control automatic function execution:
- load.json, functions listed here run once when the datapack is loaded or when
/reloadis executed. Perfect for initialization: setting scoreboards, printing a "pack loaded" message, resetting variables. - tick.json, functions listed here run every single game tick (20 times per second). Use this sparingly because it directly affects server performance. Good for checking player positions, updating scoreboards, or running conditional logic.
Create data/minecraft/tags/functions/load.json:
{
"values": [
"my_namespace:init"
]
}
And data/minecraft/tags/functions/tick.json:
{
"values": [
"my_namespace:tick_loop"
]
}
Now create the corresponding function files. init.mcfunction might set up scoreboards. tick_loop.mcfunction might check if any player is below Y=-64 and teleport them to spawn.
Testing and debugging
After making changes, run /reload to refresh. Use these commands to debug:
/datapack list, confirms your pack is loaded and enabled./function my_namespace:test, manually run a function to verify output.- Check the server log (console or
latest.log), JSON parsing errors appear there when a file is malformed. /give @s knowledge_book, this item, when given by a recipe datapack, auto-unlocks all recipes in the recipe book, which helps verify custom recipes loaded.
Namespace conventions and best practices
Never put your custom content in the minecraft namespace unless you are intentionally overriding vanilla files. If you accidentally create data/minecraft/recipes/stick.json, you will replace the vanilla stick recipe for every player.
Use descriptive file names: zombie_extra_loot.json is better than fix2.json. Group related functions in subdirectories: functions/admin/ban_warning.mcfunction, functions/events/start_race.mcfunction.
When your datapack grows complex, consider splitting it into multiple smaller packs. This makes it easier to disable individual features without rewriting a monolith. The combining datapacks guide covers how to manage multi-pack setups cleanly.
Need a server that handles this? Astroworld Hosting runs NVMe SSD, Pterodactyl panel, and DDoS protection. See features , plans from €6.39/mo.