Fabric Modding: Introduction to Loot Tables

This post was written for Minecraft Java 1.21.6 and Fabric Loader 0.16.14.

Once your custom blocks are craftable and placeable in the world, the next step is making sure they behave the way players expect—especially when broken. That’s where loot tables come in.

Loot tables are data files that tell Minecraft what items should drop when a block is broken, a mob is killed, or a chest is opened. In this post, we’ll start with the most common use case for modders: block drops.

By default, if you create a custom block without a loot table, it won’t drop anything when broken—even with Silk Touch or the correct tool. That’s because Minecraft doesn’t assume any behavior unless you define it explicitly. To fix that, we’ll create a simple loot table for our bubble_gum_block so it drops itself when harvested.

Later, you can get more advanced—adding tool conditions, custom item drops, multiple outputs, or even random loot chances—but for now, we’ll stick to the basics to get your custom block behaving like a proper part of the game.

Let’s dive in!

Previously we added recipes to all our mod items.

Where Loot Table Files Go

Loot tables, like the crafting recipes before it, are also data-driven, so they live in the data/ folder in your mod’s resources. For example, the loot table for our bubble gum block would be placed at:

src/main/resources/data/strawberry/loot_table/blocks/bubble_gum.json

Each loot table must be in the data/yourmodid/loot_table directory, in its own special folder based on the type of loot table. For blocks, this this found in data/yourmodid/loot_table/blocks and each file must be named to match the block purportedly being broken.

Loot Tables: Block Drops

File: blocks/bubble_gum_block.json

{
  "type": "minecraft:block",
  "pools": [
    {
      "bonus_rolls": 0.0,
      "conditions": [
        {
          "condition": "minecraft:survives_explosion"
        }
      ],
      "entries": [
        {
          "type": "minecraft:item",
          "name": "strawberry:bubble_gum_block"
        }
      ],
      "rolls": 1.0
    }
  ],
  "random_sequence": "minecraft:blocks/bubble_gum_block"
}

type: "minecraft:block"
Declares this as a loot table used when a block is broken. This is required for all block drops.

pools: One entry
Minecraft allows multiple pools (sets of drops), but here we only use one simple pool to define that the block drops itself.

rolls: 1.0
This means one roll attempt is made when the block is broken. It ensures that the drop is tried exactly once.

bonus_rolls: 0.0
No bonus rolls are applied. This is often used with enchantments like Fortune, but here we’re keeping things simple—just one guaranteed roll attempt.

conditions: [ "minecraft:survives_explosion" ]
This condition checks whether the block survives an explosion. If the block is blown up (by creepers, etc.), Minecraft uses the formula 1 / explosion_power to determine drop chance.

  • For example, Creeper explosions have power 3 → the block has a 33.\overline{3}\% chance to drop its loot.
  • This condition prevents drops in explosion cases unless the survival roll passes.
  • If the world was created in 1.21 or later versions of Minecraft, game rule tntExplosionDropDecay is set to true and TNT explosions will drop all affected blocks

entries:

  • type: "minecraft:item"
    This pool drops an item instead of executing an alternate behavior.
  • name: "strawberry:bubble_gum_block"
    This is the exact item ID that will be dropped when the block is broken—the block drops itself, just like basic vanilla blocks (e.g. dirt, planks, etc.).

random_sequence: "minecraft:blocks/bubble_gum_block"
This tag helps Minecraft produce consistent randomness when needed (useful in things like structure generation or replays).
However, since there’s no randomness in this table (just one guaranteed roll with no variance), you could safely omit this line with no effect.

An ingame screenshot of blocks that were dropped... and a creeper.
You can see the blocks that drop when I punch them in survival mode—hang on, someone has arrived to help!
The creeper is gone, and a few more blocks have been destroyed, with some being dropped.
Well… you can see that a few of the blocks survived to be dropped.

Modifying an Entity Loot Table: Witch Drops Bubble Gum

So far we’ve covered loot tables for blocks—but you can also control what mobs drop when they die. These are called entity loot tables, and they work just like block loot tables, except they live in a different folder and follow slightly different rules.

Let’s take a look at the loot table for the Witch, which is normally found at:

data/minecraft/loot_table/entities/witch.json

You should never modify the vanilla Minecraft data directly. Instead, use a data pack or override it with your mod’s data/minecraft/loot_table/... path.

Explaining the Witch Loot Table (with Bubble Gum)

In this section, we’re overriding the Witch’s default loot table to add a small chance of dropping our custom bubble_gum item. This file replaces the original witch.json loot table in Minecraft’s base game data, so we’ve reproduced its contents with one key addition: our gum drop!

Before we begin:
Don’t copy this code block verbatim!
We’ve abbreviated it to focus on the important parts. In practice, you’ll need to preserve all of the Witch’s original drops to avoid losing them.

Here’s how to add strawberry:bubble_gum as an additional item the Witch can drop when defeated:

{
  "type": "minecraft:entity",
  "pools": [
    {
      "bonus_rolls": 0.0,
      "entries": [
        {
          "type": "minecraft:item",
          "functions": [
            {
              "add": false,
              "count": {
                "type": "minecraft:uniform",
                "max": 2.0,
                "min": 0.0
              },
              "function": "minecraft:set_count"
            },
            {
              "count": {
                "type": "minecraft:uniform",
                "max": 1.0,
                "min": 0.0
              },
              "enchantment": "minecraft:looting",
              "function": "minecraft:enchanted_count_increase"
            }
          ],
          "name": "minecraft:glowstone_dust"
        },
        //minecraft:sugar entry
        //minecraft:spider_eye entry
        //minecraft:glass_bottle entry
        //minecraft:gunpowder entry
        //minecraft:stick entry
        //minecraft:redstone entry
        {
          "type": "minecraft:item",
          "functions": [
            {
              "add": false,
              "count": {
                "type": "minecraft:uniform",
                "max": 2.0,
                "min": 0.0
              },
              "function": "minecraft:set_count"
            },
            {
              "count": {
                "type": "minecraft:uniform",
                "max": 1.0,
                "min": 0.0
              },
              "enchantment": "minecraft:looting",
              "function": "minecraft:enchanted_count_increase"
            }
          ],
          "name": "strawberry:bubble_gum"
        }
      ],
      "rolls": {
        "type": "minecraft:uniform",
        "max": 3.0,
        "min": 1.0
      }
    },
    {
      "bonus_rolls": 0.0,
      "entries": [
        {
          "type": "minecraft:item",
          "functions": [
            {
              "add": false,
              "count": {
                "type": "minecraft:uniform",
                "max": 8.0,
                "min": 4.0
              },
              "function": "minecraft:set_count"
            },
            {
              "count": {
                "type": "minecraft:uniform",
                "max": 1.0,
                "min": 0.0
              },
              "enchantment": "minecraft:looting",
              "function": "minecraft:enchanted_count_increase"
            }
          ],
          "name": "minecraft:redstone"
        }
      ],
      "rolls": 1.0
    }
  ],
  "random_sequence": "minecraft:entities/witch"
}

Loot Table Structure Overview

Here’s how the full loot table is structured:

{
  "type": "minecraft:entity",
  "pools": [ ... ],
  "random_sequence": "minecraft:entities/witch"
}

type: This tells Minecraft the loot table is for an entity.

random_sequence: Helps control randomness for consistent loot generation. For entities, it’s best to match the path of the loot table (entities/witch). You can safely omit this if not needed for reproducibility.

Pool 1: Random Mob Drops

The first pools entry is where most of the Witch’s drops come from—including our new gum drop.

"rolls": {
  "type": "minecraft:uniform",
  "min": 1.0,
  "max": 3.0
}
  • This means the game will attempt 1 to 3 rolls from this pool per kill.
  • Each roll randomly selects an item from the pool’s entries.
  • Each roll picks one item from the pool at random, weighted by item weights (if set).

Bubble Gum Entry

Here’s our gum drop added to this pool:

{
  "type": "minecraft:item",
  "functions": [
    {
      "add": false,
      "count": {
        "type": "minecraft:uniform",
        "max": 2.0,
        "min": 0.0
      },
      "function": "minecraft:set_count"
    },
    {
      "count": {
        "type": "minecraft:uniform",
        "max": 1.0,
        "min": 0.0
      },
      "enchantment": "minecraft:looting",
      "function": "minecraft:enchanted_count_increase"
    }
  ],
  "name": "strawberry:bubble_gum"
}
  • type: It’s an item drop.
  • name: The full ID of our gum item (strawberry:bubble_gum).
  • functions: Functions in this context control the logic of how much gum is dropped if the game rolls it. These can get ridiculously complicated, but this basic setup will work for most mobs. minecraft:set_count determines the normal distribution of items in the drop, ranging from 0 to 2, while minecraft:enchanted_count_increase gives the mob the chance to drop an additional item if hit with an applicable enchantment. These functions can have additional parameters, like add or count.

Together, this makes gum a rare, fun bonus drop—never guaranteed, but sometimes rewarding.

Pool 2: Always Drops Redstone

The second pool is much simpler and handles guaranteed redstone drops:

{
  "bonus_rolls": 0.0,
  "entries": [
    {
      "type": "minecraft:item",
      "functions": [
        {
          "add": false,
          "count": {
            "type": "minecraft:uniform",
            "max": 8.0,
            "min": 4.0
          },
          "function": "minecraft:set_count"
        },
        {
          "count": {
            "type": "minecraft:uniform",
            "max": 1.0,
            "min": 0.0
          },
          "enchantment": "minecraft:looting",
          "function": "minecraft:enchanted_count_increase"
        }
      ],
      "name": "minecraft:redstone"
    }
  ],
  "rolls": 1.0
}

This pool rolls once every time the Witch dies. Its only entry drops redstone:

  • Between 4 and 8 redstone normally.
  • Looting adds up to +1 redstone.

We left this pool unchanged, so the Witch still drops its signature redstone dust.

Summary

KeyDescription
type"minecraft:entity" – for entity loot tables.
poolsA list of drop pools. Each one rolls separately.
rollsNumber of times Minecraft rolls for a drop from this pool.
entriesItems or logic used for each roll.
functionsModify drop behavior (like quantity or loot enchant scaling).
random_sequenceOptional identifier for deterministic loot RNG.

In our case:

  • We added the gum drop to Pool 1, alongside other variable drops like bottles, sticks, and spider eyes.
  • We preserved all vanilla drops, including the second pool’s redstone, to avoid breaking core game behavior.
  • We made gum relatively rare, using a small uniform count and low Looting bonus.

This kind of loot table editing is a powerful tool—but it should be used carefully, especially when overriding vanilla files.

You might have noticed that the Witch’s droppable potions aren’t mentioned anywhere in the loot table. That’s because some drops—like items a mob is holding or wearing—are handled by special in-game logic triggered when the mob dies. These kinds of drops aren’t defined in loot tables and fall outside the scope of this tutorial.

Overriding Vanilla Data with Your Mod

Up to this point, our mod has only added new content—like blocks, items, and recipes. But changing mob drops works a bit differently. When we modify the Witch’s loot table, we’re not just adding something new—we’re replacing one of Minecraft’s built-in data files.

To do this, your mod needs to exactly mimic the path of the vanilla file you want to override:

src/main/resources/data/minecraft/loot_table/entities/witch.json

✅ Yes, your mod now contains a top-level minecraft/ folder under data/! This structure ensures your file overrides the vanilla Witch loot table when the game loads data.

Why This Matters

Overriding Minecraft’s built-in data comes with a few important implications:

Compatibility

If any other mod or data pack also tries to override witch.json, only one version will be used—usually whichever one loads last. That means your mod might silently erase another mod’s changes, or vice versa.

No Merge Support

Minecraft doesn’t support partial edits or merging for loot tables. When you override a loot table, you must include all the entries you want, including any vanilla drops you still want to keep.

That’s why our example file includes all of the Witch’s original drops, and simply adds bubble gum as an additional entry.

Safer Alternatives (Advanced)

If you want to add drops without replacing the whole loot table, there are better options—but they require a bit more setup. Typically, loot table pools are immutable after they are built, so it’s easiest to use Fabric to add a third loot pool to the witch that includes bubble gum. Here’s something I added to the initialize method in ModItems (if you do this, make sure you delete witch.json):

LootTableEvents.MODIFY.register((key, tableBuilder, source, registries) -> {
    if (Identifier.of("minecraft", "entities/witch").equals(key.getValue())
            && source.isBuiltin()) {
        LootPool.Builder pool = LootPool.builder()
                .rolls(UniformLootNumberProvider.create(0,2))
                .with(ItemEntry.builder(BUBBLE_GUM))
                .apply(SetCountLootFunction.builder(
                        UniformLootNumberProvider.create(0,2)).build())
                // Credit to Yeleefff for pointing this class out to me
                .apply(EnchantedCountIncreaseLootFunction.builder(
                        registries, UniformLootNumberProvider.create(0.0f, 1.0f)));
        tableBuilder.pool(pool);
    }
});

What This Code Does

  • Registers a loot table event – Hooks into LootTableEvents.MODIFY to inspect and modify loot tables during loading.
  • Targets the Witch loot table only – Checks that the loot table is for minecraft:entities/witch and that it comes from the vanilla game (source.isBuiltin()), avoiding conflicts with other mods.
  • Creates a new loot pool – A custom pool is built to inject additional drops without replacing vanilla behavior.
  • Sets roll count between 0 and 2 – The game randomly chooses how many times (0–2) to attempt rolling drops from this pool.
  • Adds bubble gum as the drop item – Uses ItemEntry.builder(BUBBLE_GUM) to insert your modded item into the loot table.
  • Applies a random drop countSetCountLootFunction sets the bubble gum to drop in random amounts from 0–2.
  • Adds Looting bonus supportEnchantedCountIncreaseLootFunction adds extra drops if the killer’s weapon has the Looting enchantment, using a uniform range of 0.0–1.0 per level.
  • Injects the new pool into the table – The final .pool(pool) call adds your custom pool to the existing Witch loot table.

Why This Is Good Practice

  • You’re preserving vanilla behavior by not overriding existing pools.
  • The behavior is configurable and influenced by Looting, just like vanilla.
  • You used registry-safe lookup for enchantment logic via EnchantedCountIncreaseLootFunction.
  • This is forward-compatible with other mods that might also edit Witch drops.

A Note on Depth

This might feel like a lot—because it is. For what started as a beginner-friendly dive into Fabric modding, we’ve already gone deeper than many new modders ever expect to. And yet… we’ve only just scratched the surface.

Loot tables in Minecraft are an incredibly rich and flexible system. You can control everything from block drops and mob rewards to treasure chests, Trial Chambers, fishing, bartering, equipment, and even custom advancement rewards. The examples we’ve covered so far barely hint at the full range of what’s possible.

In a future post, I’d love to explore this topic in more depth and give loot tables the full treatment they deserve. But for now, I strongly encourage you to do your own research. Minecraft’s data-driven design rewards curiosity, and the Minecraft Wiki is a fantastic technical resource—not just for loot tables, but for many of the other systems we’ve touched on in this series.

If you find yourself wondering “Wait, could I make this drop enchanted gear?” or “What if I want a mob to drop custom items only under certain conditions?” — the answer is very likely yes, and it probably involves loot tables.

What’s Next?

In our next post, we’ll take a closer look at item behavior—specifically how to make your custom bubble gum edible. We’ll cover how food items work in Minecraft, how to define hunger and saturation values, and how to add effects that trigger when the item is consumed.

See you then.

No Comments on Fabric Modding: Introduction to Loot Tables

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.