Getting started

A friendly, correct-from-the-first-line walkthrough: build your first Minecraft Java data pack for 1.21+, wire a function to run on load and every tick, reload it in-game, and troubleshoot when nothing happens.

What you'll build

A tiny data pack called my_pack that prints a message once when the world loads and quietly counts every game tick. By the end you'll understand the four moving parts every pack shares: the pack.mcmeta file, a namespace, a .mcfunction, and the #minecraft:load / #minecraft:tick function tags.

πŸ“Œ Note

This guide targets Minecraft Java Edition 1.21+, which uses singular folder names (function, not functions) and pack_format 48. If you're on an older version, see pack_format history for the right number and the plural folder names.

Step 1 β€” Create the folder

In your world's save directory, open the datapacks/ folder and make a new folder for your pack. On a typical singleplayer install:

.minecraft/saves/<your world>/datapacks/my_pack/

Everything else lives inside my_pack/. You can also add the pack during world creation on the "More" → "Data Packs" screen, but dropping the folder into an existing world's datapacks/ is the fastest way to iterate.

Step 2 β€” Write pack.mcmeta

Every pack needs exactly one file at its root: pack.mcmeta. It's a small JSON file that tells Minecraft which format your pack is written for and what to show on hover.

my_pack/pack.mcmeta

{
  "pack": {
    "pack_format": 48,
    "description": "My first data pack"
  }
}

"pack_format": 48 is the value for 1.21. The description is the text shown when you hover the pack in the list β€” set it to something, even trivial, so it doesn't look broken.

⚠ Gotcha

JSON does not allow trailing commas or comments. If Minecraft refuses your pack or drops into Safe Mode, a malformed pack.mcmeta is the usual culprit β€” paste it through a JSON validator.

Step 3 β€” Make a namespace

All of a pack's content lives under data/<namespace>/. A namespace is your project's unique prefix; use a lowercase, snake_case name that won't collide with other packs. We'll use my_pack:

my_pack/
β”œβ”€β”€ pack.mcmeta
└── data/
    └── my_pack/          # <- your namespace
        └── function/     # .mcfunction files go here (singular folder!)

Files you place here get an ID of the form namespace:path. So data/my_pack/function/hello.mcfunction becomes the function my_pack:hello. Valid namespace/path characters are a-z 0-9 _ - . (and / in the path only).

πŸ’‘ Best practice

Reserve the special minecraft namespace only for overriding vanilla files and for the load/tick tag files in Step 5. Keep your content under your own namespace.

Step 4 β€” Write your first function

A function is a plain-text .mcfunction file: one command per line, run top to bottom. Create two of them β€” a setup message and a tick counter.

data/my_pack/function/hello.mcfunction

# Runs once when the pack loads
say My first data pack is loaded!
scoreboard objectives add my_pack.ticks dummy

data/my_pack/function/loop.mcfunction

# Runs every tick β€” keep tick work light!
scoreboard players add #timer my_pack.ticks 1

Lines starting with # are comments. The hello function creates a scoreboard objective; the loop function bumps a fake player counter once per tick.

Step 5 β€” Hook it to load & tick

Minecraft runs your functions automatically when they're listed in two special vanilla function tags: #minecraft:load (once on world load and after every /reload) and #minecraft:tick (every tick, 20 times a second). These tag files live under the minecraft namespace:

my_pack/
└── data/
    β”œβ”€β”€ my_pack/
    β”‚   └── function/
    β”‚       β”œβ”€β”€ hello.mcfunction
    β”‚       └── loop.mcfunction
    └── minecraft/
        └── tags/
            └── function/
                β”œβ”€β”€ load.json
                └── tick.json

data/minecraft/tags/function/load.json

{
  "values": [ "my_pack:hello" ]
}

data/minecraft/tags/function/tick.json

{
  "values": [ "my_pack:loop" ]
}

These are ordinary tags, so if another pack also adds to #minecraft:load, both packs' entries are kept (tags merge). See Tags for the full merge/replace rules.

πŸ’‘ Best practice

Keep the tick function a thin dispatcher and put real work behind conditions β€” a heavy #minecraft:tick function is the number-one cause of lag in data packs.

Step 6 β€” Reload & test

With the pack in place, load the world and run:

/reload

/reload re-reads your functions, tags, recipes, loot tables, and more without leaving the world, then fires #minecraft:load. You should see the say message appear immediately. To run a function by hand:

/function my_pack:hello

Confirm the pack is enabled and check its load order any time with:

/datapack list
/datapack list enabled
πŸ“Œ Note

/reload only hot-swaps the functions-and-data subset. Changes to world generation, dimensions, enchantments, damage types, and other dynamic registries are read only at world load β€” for those, exit and re-open the world (or restart the server).

Troubleshooting

If /reload runs but nothing happens, work down this list β€” the causes are almost always structural, not logical.

SymptomLikely cause & fix
Pack doesn't appear in /datapack listpack.mcmeta is missing, in the wrong place, or invalid JSON. It must be at the pack root, beside data/.
Pack loads but the function never runsFolder is plural (functions/, tags/functions/). On 1.21+ every folder is singular β€” rename to function/. This is the most common mistake.
"Incompatible" / "made for a different version" warningYour pack_format doesn't match the game version. It still loads if you confirm; fix the number from pack_format history.
Red error text on load, or Safe Mode promptA JSON file (often a tag or pack.mcmeta) is malformed. Fix the JSON and /reload rather than accepting Safe Mode.
/function my_pack:hello says "Unknown function"The file path or namespace doesn't match the ID. my_pack:hello must be data/my_pack/function/hello.mcfunction β€” check spelling, lowercase, and the .mcfunction extension.
Load function re-runs destructively on every reloadGate initialization behind an "is-installed" check so /reload doesn't reset state you want to keep.

Next steps

Sources