NPC skins

How Minecraft skins are stored as signed texture data, how to generate that data for any image with MineSkin, and how to apply it to Citizens NPCs (a server plugin) as well as to datapack-native NPCs — the minecraft:mannequin entity and player heads.

📌 Scope note

Citizens is a server plugin (Spigot/Paper), not a data pack — its /npc commands only exist on a server running the plugin. The skin data (texture value + signature), however, is the same everywhere, so the same MineSkin output drives a Citizens NPC, a datapack mannequin, or a player_head. Where a step is plugin-only vs. vanilla/datapack is called out below.

How a Minecraft skin is stored

A skin is not an image attached to an entity — it is a profile property named textures, carried by any player-shaped thing:

So "giving an NPC a custom skin" always reduces to obtaining a value + signature pair and putting it where that NPC reads its profile. MineSkin exists to produce that pair from any image.

Generating signed skins with MineSkin

MineSkin signs skins for you: upload a skin PNG (or point it at an existing skin/URL) and it returns a signed texture — the value, the signature, a textures.minecraft.net URL, and a UUID.

💡 Skip the upload — use the gallery

The MineSkin skins gallery is a large, browsable repository of already-signed skins. For a lot of NPCs you never need to make your own image: search/scroll the gallery, open a skin, and copy its ready-made URL (for Citizens --url) or its value + signature (for a mannequin/head or Citizens -t). Each gallery entry is already signed, so it drops straight into any of the steps below.

  1. Open mineskin.org, upload your skin image (or pick one from the gallery), and choose the model (classic / slim).
  2. Copy the outputs you need: the URL (easiest for Citizens' --url), or the value + signature (for raw application to Citizens -t, mannequins, or heads).
💡 Tip

MineSkin also has an API — POST an image and store the returned data.texture.value / data.texture.signature in a config, so a plugin or generator can set skins programmatically instead of by hand.

Applying a skin to a Citizens NPC plugin

Select the NPC (/npc select or look at it), then use one of the /npc skin forms. Easiest for a MineSkin custom skin is --url; Citizens signs and caches it for you:

# From a player's account skin
/npc skin Notch

# Always track that player's *latest* skin (lowercase L)
/npc skin Notch -l

# From an image or texture URL (MineSkin output) — recommended for custom skins
/npc skin --url https://textures.minecraft.net/texture/<hash>

# Raw signed blob (developer/console tool): uuid, value, signature
/npc skin -t <uuid> <value> <signature>
📌 Note

Only player-type NPCs render a skin. Citizens stores the skin in the NPC's data so it persists across restarts; changing it re-signs/re-caches. If a skin "won't change", it's usually a stale cache — re-run with --url or restart to clear it.

Applying a skin to a datapack NPC (mannequin / head)

For vanilla, datapack-native NPCs there is no plugin — you put the same MineSkin value/signature into the minecraft:profile profile property. This works for the minecraft:mannequin entity 1.21.9+ and for player_head items:

# A mannequin wearing a MineSkin custom skin
/summon minecraft:mannequin ~ ~ ~ {profile:{properties:[
  {name:"textures", value:"<base64 value from MineSkin>", signature:"<signature>"}
]}}

# A player head with the same skin (data-component form)
/give @s minecraft:player_head[profile={properties:[
  {name:"textures", value:"<value>", signature:"<signature>"}
]}]

If you just want an existing account's skin (not a custom image), the short form profile:{name:"Notch"} resolves it — no MineSkin needed. MineSkin is for custom images that aren't tied to a real account.

Which mobs can wear a player skin

A captured skin is a texture in the player (humanoid) UV layout. Whether a mob can wear one is decided by a single question: does its model descend from HumanoidModel? Read out of the 26.2 client jar:

PlayerModel               extends HumanoidModel<AvatarRenderState>
AbstractZombieModel       extends HumanoidModel      // zombie, husk, drowned, zombie_villager
SkeletonModel             extends HumanoidModel      // skeleton, stray, bogged, wither_skeleton, parched
VillagerModel             extends EntityModel        // NOT humanoid
IllagerModel              extends EntityModel        // NOT humanoid
WitchModel                extends EntityModel        // NOT humanoid
📍 Ceiling

There are only about nine humanoid-model mob types, and a mob texture is global per type. That is the hard limit on how many distinct player-skin-looking mobs a pack can have — and why re-skinning a desert-only mob (husk, parched) is safer than a ubiquitous one (zombie) or a wrong-biome one (stray, bogged).

Appearance vs. behaviour: mannequins, mounts and villagers

A mannequin gives you any face and no behaviour whatsoever:

net.minecraft.world.entity.Avatar    extends LivingEntity
net.minecraft.world.entity.Mob       extends LivingEntity
Mannequin                            extends Avatar

GoalSelector, PathNavigation and Brain all live on Mob. Avatar is Mob's sibling, not its parent, so a mannequin cannot walk, path, sleep, trade or fight, and no datapack registry adds AI to an entity type. Its whole NBT surface is profile, hidden_layers, main_hand, pose, immovable, description, hide_description, equipment.

So a moving NPC with a custom face is a mannequin riding an invisible mob: the mount does the wandering, the pathing and (for a villager mount) the walk home to a bed at night, while the mannequin is carried — a passenger's position comes from its vehicle, so nothing ticks on your side.

The rider height is arithmetic, not taste

EntityAttachment.PASSENGER = Fallback.AT_HEIGHT   -> (0, height, 0)
EntityAttachment.VEHICLE   = Fallback.AT_FEET     -> (0, 0, 0)
Avatar.DEFAULT_VEHICLE_ATTACHMENT = Vec3(0, 0.6, 0)   // an Avatar overrides VEHICLE

feet = mount_height × mount_scale − 0.6

Solve it for zero. For a 1.95-tall villager mount that is scale = 0.6 / 1.95 = 0.3077. Note the trap: shrinking the mount does not bring the rider closer to the ground — the mount's term falls toward zero while the rider's own −0.6 never scales, so an over-small mount buries the rider (at scale 0.0625 its feet sit 0.48 below the floor). Scaling also shrinks step height, so hand it back with the minecraft:step_height attribute or the mount cannot climb a kerb.

What the mount can and cannot lend you

Villagers specifically (26.2)

Choosing an NPC body: the decision that actually matters

Almost every argument about “custom NPCs” is really one trade-off. A captured player face and real mob behaviour do not come in the same entity unless you are prepared to build a display rig. Decide which side you need before you build anything.

BodyCaptured faceWalksBedsTrade GUIDistinct looksCost
mannequinyes, per entitynononounlimitedtrivial
re-skinned undead (husk, skeleton)texture only, globalyesnonoone per entity typelow
wandering_tradertexture only, globalyesnoyesone, everlow
villagertexture only, globalyesyesyes~15 (see below)low
six item_display rigyes, per entityyesyesyesunlimitedweeks; core shaders; breaks every version

The last row is real, not theoretical — see bradleyq/stable_player_display, which renders a full player skin from a profile across six item_display entities using a resource pack and core shaders. Judge it on maintenance, not possibility: it tracks one narrow version range at a time and its own README says the pack breaks on every Minecraft update. Pair it with Animated Java if you want a walk cycle.

Why a villager gives you variety and a trader does not

A villager’s skin is not one texture, it is four the game composites at render time:

villager/villager.png              body and face, shared by every villager
villager/type/<biome>.png          the biome costume   (desert, plains, ...)
villager/profession/<job>.png      the job clothes     (cleric, cartographer, ...)
villager/profession_level/<m>.png  the rank badge      (stone..diamond)

With 7 types and 15 professions that is a large grid of separately addressable texture slots. A wandering_trader has exactly one file (entity/wandering_trader/wandering_trader.png — note the subfolder), so every trader you spawn is the same person. If you need several different-looking merchants, that alone decides it. Repurpose a profession for its texture slot and override its trades per entity; the vanilla trades attached to that profession never appear.

Pinning a villager so it stays what you made it

The trade-cost trap: ItemCost is an EXACT component match

In NBT Offers.Recipes[].buy / buyB are ItemStackOfComponent<DataComponentExactPredicate>. Every component you name must match exactly. This is not the same operator you use in commands: a function can test a custom currency with the subset form

clear @s minecraft:gold_nugget[minecraft:custom_data~{mypack:{currency:"coin"}}] 0

but a trade cost cannot. Name the currency’s components exactly as its loot table mints them, or the merchant silently refuses the player’s own coins — no error, no log line, the trade simply never lights up. Keep the literal in one place if two systems both price in it.

The data-driven side has a cleaner answer for scoping trades without touching vanilla rosters: villager_trade entries carry a merchant_predicate, so a trade can apply only to a merchant matching a predicate (a tag, for instance). Overriding trade_set/wandering_trader/* instead repoints every trader in the world.

Re-skinning is always global — choose the mob for that

There is no per-entity texture switch for a mob in vanilla. Overriding entity/illager/evoker.png makes every evoker wear it. That is usually fine, but it should be a decision: pick mobs whose vanilla spawns do not collide with your setting (an illusioner never spawns naturally; a husk is desert-only), and expect the override to reach the whole world.

Gotchas

Sources