Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 14 — Tags: Grouping Things Together

What You’ll Build

Part V is where your data pack stops being mostly commands and starts being mostly files: JSON files that quietly tell the game what things are, what they belong to, and what should happen. The very first of those file types is the tag, and it’s a good place to start, because you’ve secretly been using tags since Chapter 9 without knowing it.

By the end of this chapter you’ll be able to write a JSON file that groups a bunch of blocks (or items, or kinds of mob, or functions) under one name, refer to that whole group with a single # word, extend a built-in Minecraft group by adding your own things to it, and test the result with a command. To prove it works you’ll add a small file to your mypack pack (a block tag called mypack:my_logs) and then write a function that checks whether the block under your feet belongs to it.

This chapter extends the pack you started in Chapter 9, your mypack pack, and uses the test world you’ve used since Chapter 1. It assumes JSON (Chapter 8), the pack’s folder layout and the minecraft:load function tag (Chapter 9), and /execute ... if block ... run from Chapter 4.

First: this is not the tag from last chapter

Chapter 13 ended with a warning, and this chapter is exactly why. The word tag means two completely different things in Minecraft, and we have now arrived at the second one. Before anything else, let’s nail the difference down so it never trips you up.

Modern Minecraft Two unrelated features share the name tag. Tutorials online almost never warn you:

  1. Entity tag (Chapter 13) — a runtime label you stick on one specific entity with the /tag command and check with @e[tag=...]. It’s a sticker on a single mob or player, added and removed live while you play. It has no # and lives in no file.
  2. Registry tag (this chapter) — a JSON file in your data pack that groups whole types of things: every kind of log block, every kind of plank, a list of functions. You refer to it with a leading #, like #minecraft:logs, and you can’t change it with a command mid-game. You edit the file and /reload.

Quick test: red_team stuck on a particular zombie is an entity tag. #minecraft:logs standing for all log block types is a registry tag. Same word, two worlds. This whole chapter is about the second kind.

The divide is clear: an entity tag is a label applied to a single entity at runtime, while registry tags are applied to entity types and can’t be changed by commands. So from here on, when you see a # in front of a name, think registry tag, a group of types defined in a file, never the on/off sticker from last chapter.

What a registry tag actually is

A registry tag is a named list of game things of one kind, grouped so you can talk about all of them at once. Tags let you group different game elements together; they reference groups of registry entries, so Minecraft treats multiple items, blocks, or entities as a single category. The built-in tag #minecraft:logs, for example, stands for all log blocks at once, so a command or recipe that uses #minecraft:logs automatically applies to oak logs, birch logs, cherry logs, and every other block on the list, with no need to spell each one out.

Two things make tags one of the most useful pieces of a data pack:

  • You name a group once and reuse it everywhere. Instead of listing twenty blocks in five different places, you list them once in a tag and point everything at #yourname:yourtag.
  • You can change how the game itself behaves by editing its tags. Minecraft’s own rules lean on built-in tags all over the place: which blocks a tool mines faster, which blocks bees pollinate, which mobs burn in sunlight. Add a block to the right vanilla tag in your pack and you bend that rule without touching a single line of the game’s code.

The word “registry” comes back from Chapter 7: a registry is the game’s master list of one kind of thing (all blocks, all items, all entity types, and so on). A registry tag is just a smaller named list picked out of one of those master lists: a group of blocks out of all blocks, a group of items out of all items. That’s why there’s a different kind of tag for each registry.

Where tags live: data/<namespace>/tags/<registry>/

Tags are files in your data pack, and they go in a tags folder. Here is the layout:

mypack/
  pack.mcmeta
  data/
    <namespace>/
      tags/
        function/
          <name>.json      a function tag
        <registry>/
          <name>.json      a tag for that registry

Inside tags/ you make one folder per registry, and its name is the registry’s name. The four you’ll meet in this chapter are:

  • tags/block/: block tags, groups of block types (like #minecraft:logs).
  • tags/item/: item tags, groups of item types (like #minecraft:planks, used by recipes that accept “any plank”).
  • tags/entity_type/: entity type tags, groups of kinds of mob (like a list that means “all zombie types”).
  • tags/function/: function tags, groups of functions, including the two special ones we’re about to revisit.

Modern Minecraft Just like the function and recipe folders from Chapter 9, these folder names are singular: block, item, entity_type, function, not blocks/items/tags/functions. Older guides (and old packs) used plural names. If your tag silently does nothing, a plural folder name is the first thing to check.

The tag’s name comes from where the file sits, exactly like every other identifier in the book. A file at data/mypack/tags/block/my_logs.json is the block tag mypack:my_logs. (A file at data/wiki/tags/block/foo/example.json makes the tag wiki:foo/example; subfolders just become part of the path, same as Chapter 8.) To use a tag, you put a # in front of that name: #mypack:my_logs. The # is what tells the game “this is a tag (a whole group), not a single block.”

The JSON inside a tag file

A tag file is a small JSON object with up to two fields. Here’s the simplest possible block tag, a group of three log blocks, saved into your pack:

data/mypack/tags/block/my_logs.json

{
  "replace": false,
  "values": [
    "minecraft:oak_log",
    "minecraft:birch_log",
    "minecraft:spruce_log"
  ]
}

Two fields, and that’s the whole format:

  • values is the list, an array (Chapter 8) of names. Each name is a thing you’re putting in the group. Here they’re three block identifiers, so this tag means “oak, birch, or spruce log.”
  • replace is a true/false switch we’ll explain in a moment. It’s optional and defaults to false, so you can leave it out, but we’ll write it in for now to make it visible.

A values list can hold three kinds of entry:

  1. A plain resource location: the name of a thing to include, like "minecraft:oak_log".
  2. Another tag, prefixed with #, which pulls in everything that tag contains. So a values list can contain "#minecraft:planks", meaning “and also every plank.” (Tags referencing tags is allowed and handy; just don’t make a loop where two tags point at each other, which causes a loading failure.)
  3. An object with options, for the one special case in the next callout.

Here’s an entry of type 2 in action, a tag that says “all my logs, plus everything already in the vanilla planks group”:

data/mypack/tags/block/my_building_blocks.json

{
  "replace": false,
  "values": [
    "#mypack:my_logs",
    "#minecraft:planks"
  ]
}

Because both entries start with #, this tag is built entirely out of other tags: it folds in your own mypack:my_logs and the built-in minecraft:planks and treats them as one big group. That’s the “name a group once, reuse it everywhere” idea taken one step further: groups made of groups.

Under the Hood The third kind of values entry is an object with two fields, used when you want to list something that might not exist (say, a block from a mod the player may not have installed):

{
  "values": [
    { "id": "mypack:future_block", "required": false }
  ]
}

Here, id is the name (in any of the formats above), and required decides whether the whole tag fails to load if that entry is missing. It’s true by default; setting it to false means “skip this one quietly if it isn’t found” instead of breaking the tag. You won’t need this often as a beginner, but it’s why you sometimes see entries written as objects rather than plain strings. (Skippable.)

The big reveal: load and tick were tags all along

Open up your pack and look at a file you wrote back in Chapter 9:

data/minecraft/tags/function/load.json

{
  "values": [
    "mypack:load",
    "mypack:score_setup"
  ]
}

Look at the path. Look at the contents. It’s a file in tags/function/, with a values list of function names. It’s a registry tag (a function tag), and you’ve been writing them since your very first data pack. You just didn’t have the word for it yet.

Here’s what these two special function tags do: functions tagged in the minecraft:tick tag run every tick at the start of the tick, and functions tagged in minecraft:load run once at the start of the tick after a server (re)load. That’s exactly the behavior you’ve relied on. The file at data/minecraft/tags/function/load.json is the minecraft:load tag (it’s in the minecraft namespace because that’s the folder it sits in), and listing mypack:load in its values is how you told the game “run my load function when the pack loads.” Same machinery for the tick tag you started in Chapter 11:

data/minecraft/tags/function/tick.json

{
  "values": [
    "mypack:kill_on_gold",
    "mypack:timer_tick"
  ]
}

Every function you’ve ever hooked into “run on load” or “run every tick” you did by adding its name to a values list in a function tag. The only thing new in this chapter is the name for what you were doing, plus the fact that the same trick works for blocks, items, and mob types too.

Under the Hood Function tags run their functions in the order of their first appearance in a tag, and if the same function is listed twice (directly or through a sub-tag) it still runs only once. For load and tick that ordering is the one place where the order of a tag’s values actually matters. For most tags (which only ever get asked “is this thing in the group, yes or no?”) the order is irrelevant. (Skippable.)

Extending a vanilla tag vs. replacing it

Now back to that replace field, because it controls the single most powerful thing tags can do: quietly add your stuff to a built-in Minecraft group.

Here’s the key rule. When two data packs (and remember, vanilla itself is a data pack) define a tag with the same name:

  • If replace is false (or left out, which is the default), your values are added on top of what’s already there. You extend the group.
  • If replace is true, your tag completely replaces the lower-priority one. You wipe out everything that was in it and start fresh with only your values.

So to add a block to a vanilla tag, you make a file with the same name as the vanilla tag (same namespace, same path) and leave replace off. Watch: this adds cherry logs to Minecraft’s own #minecraft:logs group, without disturbing the dozens of logs already in it:

data/minecraft/tags/block/logs.json

{
  "replace": false,
  "values": [
    "minecraft:cherry_log"
  ]
}

Notice the namespace is minecraft, not mypack: the file lives at data/minecraft/tags/block/logs.json because it’s the minecraft:logs tag you’re extending. With replace false, oak, birch, spruce, and the rest stay exactly where they are; your one line just joins the party. From now on, anything in the game that checks #minecraft:logs will treat cherry logs as a log too.

Flip replace to true and the meaning changes completely:

data/minecraft/tags/block/logs.json

{
  "replace": true,
  "values": [
    "minecraft:cherry_log"
  ]
}

This version says “#minecraft:logs now means only cherry log, forget everything else.” Every other log type falls out of the tag, and any game behavior tied to #minecraft:logs suddenly ignores them. That’s almost never what you want for a vanilla tag, but it’s exactly what you want when you’re defining your own fresh tag and don’t care about merging with anything. Rule of thumb: extend (false / leave it off) when touching a vanilla tag; only reach for replace: true when you mean to throw the old contents away.

A few vanilla tags worth knowing, all real groups you can extend or point at:

  • #minecraft:logs: every log block.
  • #minecraft:planks: every plank block (and the matching item tag is used by recipes that take “any plank”).
  • #minecraft:leaves, #minecraft:wool: leaves and wool blocks.
  • #minecraft:swords: the item tag grouping all swords.

Walkthrough: build and test a block tag

Let’s put it all together. You’ll make a block tag and then write a command that checks it.

Step 1 — create the tag. If you followed along above, you already have it; if not, save this:

data/mypack/tags/block/my_logs.json

{
  "replace": false,
  "values": [
    "minecraft:oak_log",
    "minecraft:birch_log",
    "minecraft:spruce_log"
  ]
}

Step 2 — write a function that tests it. Back in Chapter 4 you learned /execute ... if block ... run, which checks the block at a position. The block you test for can be a single block ID or a block tag, and to use a tag you write it with a #, just like everywhere else. This function checks whether the block one space below you is in your my_logs group:

data/mypack/function/tag_check.mcfunction

# Is the block under my feet one of my logs?
execute if block ~ ~-1 ~ #mypack:my_logs run say Standing on a my_logs block!

The position ~ ~-1 ~ is “right where I am, but one block down” (relative coordinates from Chapter 2), and #mypack:my_logs is your tag. The command succeeds (and the say runs) only if the block down there is oak, birch, or spruce log, because those are the three types your tag groups.

Step 3 — try it. Run /reload so the game reads your new files, place an oak log, stand on top of it, and run the function from chat with /function mypack:tag_check. You should see the message. Step off onto plain dirt and run it again: silence, because dirt isn’t in the tag. One tag, three block types, one tidy check.

Figure (to be captured). chat shows “Standing on a my_logs block!” after running mypack:tag_check while the player stands on top of an oak log block

Try It! Add a fourth log to your tag’s values (say "minecraft:cherry_log"), /reload, and your same tag_check function now recognizes cherry logs too. You changed what the check matches without touching the function at all. That’s the whole point of tags: edit the group in one place, and everything that points at #mypack:my_logs updates at once.

Practice

  1. Make an item tag. Create data/mypack/tags/item/my_gems.json grouping "minecraft:diamond" and "minecraft:emerald". Item tags can be searched in the Creative inventory by typing #mypack:my_gems in the search box; try it and watch both gems appear.

  2. Extend a real vanilla group. Pick a vanilla tag from the list above and add a block to it the extend way (matching namespace and path, replace left off). For example, drop something into data/minecraft/tags/block/wool.json. /reload and confirm the original contents are still there by checking a block that was already in the tag.

  3. Build a tag out of tags. Write a block tag whose values are nothing but two # references (one to your own mypack:my_logs and one to a vanilla tag) and use it in a tag_check-style function. Confirm that the combined group matches blocks from both source tags.

What Can Go Wrong

  • You forgot the #. Inside a command, #mypack:my_logs means “the tag,” but mypack:my_logs with no # means “a single block named mypack:my_logs,” which doesn’t exist, so the command errors or never matches. The # is what turns a name into a group. (And the opposite mistake from last chapter: don’t put a # on an entity tag; @e[tag=red_team] never takes one.)

  • replace: true wiped a vanilla tag. If a built-in behavior suddenly stops working after you edited a vanilla tag (bees ignoring your flowers, a tool no longer mining fast), check whether you left replace set to true. On a vanilla tag that throws away everything the game put there. Switch it to false (or delete the line) so your entry is added instead of substituted, then /reload.

  • Wrong folder, or a plural folder name. A block tag must sit in tags/block/, an item tag in tags/item/, and so on. Put it in the wrong registry folder and it groups the wrong kind of thing (or nothing). And the folder names are singular: tags/block/, never tags/blocks/. A silently-ignored tag is almost always a misspelled or mis-pluralized folder.

  • An entry doesn’t exist and the whole tag breaks. If a name in values points at something the game can’t find, loading the tag fails. Either fix the spelling, or, if it’s deliberately optional, use the object form { "id": "...", "required": false } so that one missing entry is skipped instead of breaking the rest.

What You Know Now

You can now write a registry tag: a JSON file under data/<namespace>/tags/<registry>/ that groups types of things (blocks, items, entity types, or functions) under one name you address with a #. You know the format is just values (the list) and an optional replace flag; that values entries can be plain names, other tags prefixed with #, or { "id": ..., "required": false } objects; and that replace: false extends a tag while replace: true replaces it, which is how you safely add your own blocks to Minecraft’s own groups. You finally know what those load.json and tick.json files have been all along: function tags, the same mechanism wearing a special name. And you can test a block tag in-game with /execute if block ~ ~-1 ~ #mypack:my_logs. Tags are the quiet backbone of the declarative files coming next (recipes, loot tables, advancements, and more all lean on them), so this is the tool that makes the rest of Part V click.