← All articles

Full Minecraft 1.21.4 Migration Guide: Why Old Resource Packs Break and How to Rebuild Them

Minecraft 1.21.4 does not just rename a few tags. It changes the whole mental model behind custom items. If your old 1.20 workflow was built around models/item, numeric CustomModelData, and hand-written NBT blobs, this update is the moment where all of that starts to crack at once.

Open resource-pack generator

Why old 1.20 packs suddenly feel broken

The most misleading thing about 1.21.4 is that your pack may look half-alive. Textures still exist. PNGs still open. Some models may still seem to work in limited cases. That makes the migration feel like a small syntax annoyance, when in reality Mojang changed the layer that decides which model an item should use.

In older workflows, creators often thought like this: "I have an item model in models/item. Inside that file I place overrides. Each override watches some predicate like custom_model_data, and then redirects to another model." That logic is no longer the center of the system.

In 1.21.4, item rendering shifts into item model definitions stored in assets/<namespace>/items/. At the same time, item payloads stop behaving like old NBT compounds and move into explicit Data Components. So the migration is not just "fix the path" or "change one give command." It is really a migration from one architecture to another.

What changed at a glance

Old 1.20 habit1.21.4 realityWhy it matters
assets/<ns>/models/item/...assets/<ns>/items/... chooses the logicThe selection layer is now separate from the raw model.
overrides in item model JSONminecraft:select, minecraft:range_dispatch, minecraft:conditionYou now describe routing logic with dedicated model-definition types.
{CustomModelData:12}minecraft:custom_model_data componentIdentity is component-driven, not an ad-hoc NBT tag.
display:{Name:...,Lore:...}minecraft:custom_name, minecraft:loreVisible item presentation is broken into named components.
Numeric-only model identityStrings, flags, floats, colors inside custom_model_dataOne item can now carry richer state than just one integer.

The folder move is real, but the deeper change is conceptual

Yes, you really do move from models/item logic toward the items folder. But the more important shift is this: the game no longer assumes your raw item model should also be the place where all render-choice logic lives.

That split is actually healthier. It means you can treat the raw model as shape and art, and treat the item definition as the dispatcher that decides when that art is shown.

A minimal old vs new example

// Old 1.20-style item model
{
  "parent": "item/generated",
  "textures": {
    "layer0": "minecraft:item/paper"
  },
  "overrides": [
    {
      "predicate": { "custom_model_data": 12 },
      "model": "cubeinsquare:item/paper/pass"
    }
  ]
}

In the old system, the same file both defined the base item and controlled the switch into a custom variant. That is exactly the pattern 1.21.4 moves away from.

// New 1.21.4-style items definition
{
  "model": {
    "type": "minecraft:select",
    "property": "minecraft:custom_model_data",
    "index": 0,
    "cases": [
      {
        "when": "pass_document",
        "model": {
          "type": "minecraft:model",
          "model": "cubeinsquare:item/paper/pass"
        }
      }
    ],
    "fallback": {
      "type": "minecraft:model",
      "model": "minecraft:item/paper"
    }
  }
}

Line-by-line explanation of the new definition

"model": this is the root routing object. Instead of dumping render logic into old overrides, you now declare an item model definition object.

"type": "minecraft:select": this tells the game to choose between discrete cases. Use this when the item should jump between named states like "pass," "ticket," "permit," or "ritual seal."

"property": "minecraft:custom_model_data": the selection source is the custom_model_data component, not the legacy predicate field.

"index": 0: the game reads the first string from the relevant list inside the component. This matters because the component can now hold more than one kind of payload.

"cases": these are your explicit branches. Each case says, "when the value is this, render that."

"when": "pass_document": this is a string key, not an old numeric ID. That makes your intent much easier to read in commands, loot tables, or villager trades.

"type": "minecraft:model": this says the selected branch should render a plain raw model from the models directory.

"model": "cubeinsquare:item/paper/pass": this points to the actual raw item model, the same kind of art asset you already know how to build.

"fallback": if nothing matches, the item still needs a safe base appearance. Without a fallback, you are effectively inviting missing-model chaos.

Commands break for the same reason

The command layer and the pack layer break together because they used to depend on the same old assumptions. Here is the shape of the transition:

// Old style
/give @p minecraft:paper{CustomModelData:12,display:{Name:'{"text":"Pass"}'}} 1

// New style
/give @p minecraft:paper[
  minecraft:custom_model_data={strings:["pass_document"]},
  minecraft:custom_name='[{"text":"Pass"}]'
] 1

Line-by-line explanation of the new command

minecraft:paper: the vanilla base item still matters. Your pack logic decorates or redirects a real item, it does not create a new registry item.

minecraft:custom_model_data={...}: the identity payload now sits in a named component block.

strings:["pass_document"]: instead of encoding meaning into a number like 12, you can now write a human-readable state key. That is much friendlier for long-term maintenance.

minecraft:custom_name: visible text becomes its own component. In older workflows, creators often buried everything inside display; now the data is flatter and easier to reason about.

What should you audit first on a real server

This is why migration often feels bigger than the pack itself. The ZIP may survive. The staff workflow is what usually collapses first.

Typical migration mistakes

  1. Only changing commands, not pack logic. You can convert /give syntax and still forget that old model routing also needs to move.
  2. Treating the items folder like a rename-only patch. It is not just a new folder. It is a new routing layer.
  3. Leaving no fallback model. That makes debugging much harder because "nothing shows" and "wrong branch selected" start to look the same.
  4. Keeping old numeric IDs without reconsidering meaning. 1.21.4 gives you strings, flags, floats, and colors. Use that extra expressiveness.
  5. Testing only in inventory. The new item model system can also react to context, hand, charge, time, and other states, so one view is not enough.

What the migration gives you in return

The change is painful, but it also opens cleaner design space. The new item system is much closer to describing meaning than patching around engine quirks. Once you stop thinking in old NBT blobs, you gain a better vocabulary for dynamic items.

You can separate visible text from hidden identity. You can route models through named string states instead of remembering random numbers. You can use range_dispatch for durability-driven states and select for symbolic states. And you can start treating item rendering like a deliberate system instead of one huge pile of overrides.

Practical conclusion

If your old 1.20 setup is breaking, the fix is not "find the one wrong tag." The fix is to migrate your whole item workflow into the new architecture:

  1. Move item-selection logic into assets/<namespace>/items
  2. Replace old overrides thinking with item model definitions
  3. Move names, lore, and hidden identity into proper components
  4. Retest commands, villager trades, and pack routing together

Once you do that, 1.21.4 stops looking like a random breaking update and starts looking like the version where item identity finally becomes explicit.

FAQ

Do my textures and raw model files need to move out of models?

No. The big shift is that the selection logic moves into items. Raw models still exist in the model layer; they are simply referenced differently.

Do I have to replace every numeric CustomModelData value immediately?

Not always immediately, but 1.21.4 gives you richer fields inside the component, and strings are usually clearer for long-term server maintenance than magic numbers.

Why does my old give command fail even though the texture is still in the pack?

Because the texture was rarely the fragile part. The command payload and the model-routing logic were the pieces tied to old NBT and old override behavior.

What should I migrate first on an RP server?

Quest items, villager trade currencies, passes, permits, potion rewards, and any staff cheat-sheet commands. Those are the objects most likely to break your live gameplay if left half-migrated.

Why is this better than the old system?

Because the new system is more descriptive. It separates concerns, supports richer data, and lets you model item identity in a way that is easier to read, document, and scale.