Guidefor modders5 min read
CustomModelData to item_model: what changed and when
CustomModelData used to be one integer. It is now a component system split across item_model, a structured custom_model_data list, and per item JSON files. What changed, which version broke it, and how to migrate.
The tag is gone
CustomModelData used to be a single NBT integer. Set it on an item, point a resource pack
predicate at the same number, and the pack picked a custom model. That system does not exist
anymore.
It was replaced in two separate steps, on two separate versions, which is why searches for this turn up conflicting answers depending on which release the answer was written for:
minecraft:item_model, a component that names which item definition file the game reads for that item.minecraft:custom_model_data, still a component with a familiar name, but no longer one number. It is now a structured list of floats, flags, strings, and colors.
Neither change is cosmetic. The old predicate lookup in models/item/*.json is gone too,
replaced by a new file location and two new predicate types. A pack written for the old
system does not degrade gracefully on the new one. It does nothing.
Version timeline: what changed when
"Which version broke this" is the actual question most people have, so here is the sequence.
Before 1.20.5. Item data lived in NBT. CustomModelData was a plain integer tag, matched
by a custom_model_data predicate inside the item's model JSON.
1.20.5. Minecraft replaced NBT item data with components across the board. CustomModelData
became minecraft:custom_model_data, but it was still a single integer at this point, and the
old predicate matching in resource packs kept working. Most existing packs survived this step
without changes.
1.21.2 (snapshot 24w36a). The item_model component format was introduced. Early and
narrow: it added a way to point at a model directly, without yet changing how
custom_model_data itself was structured.
1.21.4 (snapshot 24w45a). The full item model definition system landed. custom_model_data
became the structured list described above, the new assets/<namespace>/items/*.json file
format replaced the old predicate files, and minecraft:select and minecraft:range_dispatch
became the ways to match against it. This is the release that actually breaks anything still
written for the integer tag, and it is the one most compatibility threads are really asking
about.
Point releases after 1.21.4 have not restructured the component again, but Mojang iterated on the exact give command syntax across this whole window. Verify the syntax against the specific version you are targeting rather than trusting one example as universal.
Old syntax vs new syntax
Setting the value, old system (pre 1.20.5):
/give @p minecraft:nether_star{CustomModelData:15}
Setting the value, component era (1.20.5 to 1.21.3):
/give @p nether_star[custom_model_data=15]
Still a single number, just moved from NBT into a component.
Setting the value, current system (1.21.4+):
/give @p nether_star[custom_model_data={floats:[15]}]
The number now lives inside a named list. A single item can carry several values at once, across all four list types:
/give @s bone[custom_model_data={floats:[4.0, 5.6, 99.1],strings:["foo:bar"],colors:[8323327, [0.5,0,1], 0x7FFF00FF]}]
The resource pack side changed just as much. Old predicate file, at
assets/minecraft/models/item/nether_star.json:
{
"parent": "item/generated",
"textures": { "layer0": "item/nether_star" },
"overrides": [
{ "predicate": { "custom_model_data": 15 }, "model": "custom/my_star" }
]
}
New item definition file, at assets/minecraft/items/nether_star.json:
{
"model": {
"type": "minecraft:range_dispatch",
"property": "minecraft:custom_model_data",
"fallback": { "type": "minecraft:model", "model": "minecraft:item/nether_star" },
"entries": [
{ "threshold": 15, "model": { "type": "minecraft:model", "model": "custom/my_star" } }
]
}
}
Different folder, different file name pattern, different JSON shape. There is no automatic bridge between the two.
How the new matching works
The old system had one predicate keyed to one number. The new system has two predicate types,
each reading a different list from custom_model_data:
minecraft:range_dispatch matches against the floats list. It selects the last entry
whose threshold is less than or equal to the item's value, with an optional scale applied
first. This is the direct replacement for the old numeric predicate, and it is what the
timeline example above uses.
minecraft:select matches against the strings list. It compares exact values instead
of a numeric range, with a required fallback for anything that does not match.
The flags list feeds minecraft:condition checks instead, and the colors list supplies
tint values rather than model selection at all. Every one of the four lists takes an optional
index, defaulting to 0, so a single item can carry multiple independent values of the same
type and have different predicates read different positions in the list.
None of this existed before 1.21.4. Anything describing custom_model_data as one number is
describing a version before this release.
Plugin compatibility: ItemsAdder and Oraxen
Resource pack plugins that generate these files for you had to rebuild the generation logic, not just adjust a config value, since the file location and predicate format both changed.
ItemsAdder tracked the change through two PluginBugs issues: #4224, requesting
item_model support as a CustomModelData alternative, and #4270, covering the 1.21.4
restructuring of custom_model_data itself. Both needed dedicated releases rather than a
config flag.
Oraxen covered it in issue #1616, and its current documentation describes supporting
several appearance systems side by side on 1.21.4 and later: item_properties for the
item_model component, model_data_ids for the strings list, model_data_float for the
floats list, and generate_predicates for packs that still need the legacy predicate format
for older clients.
The pattern on SpigotMC resource threads is the same across most resource-pack plugins in this space: a supported-version range that gets bumped release by release, for example "1.8 to 1.21.4" moving to "1.8 to 1.21.5" as each plugin catches up. A plugin whose stated range stops before 1.21.4 has not addressed this change yet.
Maintaining a resource pack today
Pick a floor version and be explicit about it. Everything at or after 1.21.4 uses the item definition file system. Everything before does not. Trying to write one file that satisfies both is not possible; the file location itself differs.
If you support both eras, use two file sets. Either pack overlays scoped to a
pack_format range, or two separate downloads. Resource pack format
numbers covers how overlays and supported_formats
work, and the same mechanism applies here: this is exactly the kind of layout change that
overlays exist for.
Check what the plugin actually generates before assuming. If a plugin is producing your item definition files, confirm it is on a build that targets the version you ship, not just that it runs without erroring. A stale build can still load and still generate the wrong file format silently.
Do not hand-author both an old predicate model and a new item definition and hope the game picks the right one. It does not merge them. The game reads whichever file its version expects and ignores the other entirely, so a pack with both is not more compatible, it is just carrying dead weight in one direction.

