# skinchanger (/docs/core/skinchanger)

`skinchanger` is the client-only ESX Legacy resource that applies appearance values (heritage, face features, hair, overlays, clothes, props) to the `mp_m_freemode_01` and `mp_f_freemode_01` peds and reads them back. It does not save anything; [esx_skin](/docs/core/esx_skin) handles the UI and the database.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `skinchanger` |
| Version | 1.16.3 (fxmanifest, ships with ESX Legacy 1.16.4) |
| Side | Client only |
| Dependencies | `es_extended` (locale file only) |
| Used by | `esx_skin`, `esx_multicharacter`, job and shop addons |

## Skin table [#skin-table]

A skin is a flat table of component names and integers. `sex` is required: `0` is male, `1` is female. Keys you omit keep their current value.

| Group | Keys |
| --- | --- |
| Model | `sex` |
| Heritage | `mom`, `dad`, `grandparents`, `face_md_weight`, `skin_md_weight`, `face_g_weight` |
| Face features | `nose_1`-`nose_6`, `cheeks_1`-`cheeks_3`, `lip_thickness`, `jaw_1`, `jaw_2`, `chin_1`-`chin_4`, `neck_thickness`, `eye_color`, `eye_squint` |
| Hair | `hair_1`, `hair_2`, `hair_color_1`, `hair_color_2` |
| Clothes | `tshirt_1/2`, `torso_1/2`, `decals_1/2`, `arms`, `arms_2`, `pants_1/2`, `shoes_1/2`, `mask_1/2`, `bproof_1/2`, `chain_1/2`, `bags_1/2` |
| Props | `helmet_1/2`, `glasses_1/2`, `watches_1/2`, `bracelets_1/2`, `ears_1/2` |
| Overlays | `eyebrows_1`-`eyebrows_6`, `beard_1`-`beard_4`, `makeup_1`-`makeup_4`, `lipstick_1`-`lipstick_4`, `chest_1`-`chest_3`, `bodyb_1`-`bodyb_4`, `age_1/2`, `blemishes_1/2`, `blush_1`-`blush_3`, `complexion_1/2`, `sun_1/2`, `moles_1/2` |

For props, `-1` removes the item (for example `helmet_1 = -1`).

<Callout type="info">
Old examples use `face` and `skin` keys. They no longer exist; use `mom`, `dad` and the `*_md_weight` blend keys instead.
</Callout>

## Configuration [#configuration]

| Key | Default | Description |
| --- | --- | --- |
| `Config.Locale` | `GetConvar("esx:locale", "en")` | Language of component labels. |
| `Config.Components` | 98 entries | Ordered list of editable components. esx_skin builds its menu from it. |

Each entry in `Config.Components` has these fields:

| Field | Type | Description |
| --- | --- | --- |
| `name` | `string` | Skin key, for example `"torso_1"`. |
| `label` | `string` | Translated menu label. |
| `value` | `number` | Default value used at startup. |
| `min` | `number` | Minimum value. |
| `max` | `number` \| `function(ped, character)` | Maximum value, or a function that reads drawable or texture counts from the ped. |
| `zoomOffset`, `camOffset` | `number` | Camera position in the esx_skin editor. |
| `textureof` | `string` | Optional. Marks a texture slider that depends on another component (for example `hair_2` depends on `hair_1`). |
| `componentId` | `number` | Optional. GTA component or prop index for clothing entries. |

## Exports [#exports]

All exports are client side.

### GetSkin [#getskin]

```lua
local skin = exports.skinchanger:GetSkin()
```

Returns the current skin table with every component name.

### LoadSkin [#loadskin]

```lua
local ok = exports.skinchanger:LoadSkin(skin)
```

| Name | Type | Description |
| --- | --- | --- |
| `skin` | `table` | Skin table with `sex = 0` or `sex = 1`. |

Returns `false` if the skin is invalid. When `sex` changes, the freemode model is swapped first and the skin is applied afterwards.

### LoadClothes [#loadclothes]

```lua
local ok = exports.skinchanger:LoadClothes(playerSkin, clothesSkin)
```

| Name | Type | Description |
| --- | --- | --- |
| `playerSkin` | `table` | The player's base skin (must contain `sex`). |
| `clothesSkin` | `table` | Clothing values to put on top. Face, heritage, hair and overlay (beard, makeup, eyebrows, ...) keys in this table are ignored. |

```lua title="client/main.lua"
-- put on the job uniform without touching face or hair
xLib.callback('esx_skin:getPlayerSkin', false, function(skin, jobSkin)
    local uniform = skin.sex == 0 and jobSkin.skin_male or jobSkin.skin_female
    exports.skinchanger:LoadClothes(skin, uniform)
end)
```

### Change [#change]

```lua
exports.skinchanger:Change(key, value)
```

| Name | Type | Description |
| --- | --- | --- |
| `key` | `string` | Component name. |
| `value` | `number` | New value. Changing `sex` reloads the model. |

### GetData [#getdata]

```lua
local components, maxVals = exports.skinchanger:GetData(noMax)
```

Returns `Config.Components` with current values filled in, plus a table of maximum values per component. Pass `true` as `noMax` to skip the max calculation.

### GetMaxVals [#getmaxvals]

```lua
local maxVals = exports.skinchanger:GetMaxVals()
```

Returns `{ [componentName] = max }` for the current ped.

## Events [#events]

| Event | Type | Arguments | Description |
| --- | --- | --- | --- |
| `skinchanger:loadSkin` | Net event | `skin, cb?` | Same as `LoadSkin`. `cb` only works when triggered locally. |
| `skinchanger:loadClothes` | Net event | `playerSkin, clothesSkin` | Same as `LoadClothes`. |
| `skinchanger:getSkin` | Local event | `cb(skin)` | Returns the current skin. |
| `skinchanger:getData` | Local event | `cb(components, maxVals)` | Same as `GetData`. |
| `skinchanger:change` | Local event | `key, value` | Same as `Change`. |
| `skinchanger:loadDefaultModel` | Local event | `isMale, cb?` | Sets `mp_m_freemode_01` (true) or `mp_f_freemode_01` (false). |
| `skinchanger:modelLoaded` | Local event | none | Re-applies a pending skin after a model change. |

```lua title="client/main.lua"
TriggerEvent('skinchanger:loadDefaultModel', true, function()
    TriggerEvent('skinchanger:loadSkin', { sex = 0, hair_1 = 4, hair_color_1 = 2 })
end)

TriggerEvent('skinchanger:getSkin', function(skin)
    print(json.encode(skin))
end)
```

Because `skinchanger:loadSkin` and `skinchanger:loadClothes` are net events, the server can push a skin:

```lua title="server/main.lua"
TriggerClientEvent('skinchanger:loadSkin', playerId, skin)
```

## Common issues [#common-issues]

- **Skin not applied.** The table has no valid `sex`. Always include `sex = 0` or `sex = 1`.
- **Changes are lost after relog.** skinchanger only changes the ped locally. Save through [esx_skin](/docs/core/esx_skin) exports on the server.
- **Callback of `LoadSkin` export never runs.** The export does not accept a callback. Use the `skinchanger:loadSkin` event with a callback instead.
- **Custom component not saved.** Add new components to both `Config.Components` here and `Config.SkinFields` in esx_skin.

## Related [#related]

- [esx_skin](/docs/core/esx_skin)
- [esx_multicharacter](/docs/core/esx_multicharacter)
- [Core resources](/docs/core)
