# Client PlayerData (/docs/es_extended/shared/playerdata)

`ESX.PlayerData` is the client-side copy of the local character: identifier, job, accounts, inventory, loadout, metadata and a few live values like the ped. The server sends it on `esx:playerLoaded` and pushes updates through secure events. This page explains the fields and how to keep your copy in sync.

## Overview [#overview]

| | |
|---|---|
| Resource | `es_extended` |
| Side | Client (server code uses the [xPlayer](/docs/es_extended/server/xplayer) object) |
| Kept in sync by | `@es_extended/imports.lua` |

For the server-side player model and database columns see [Player data](/docs/es_extended/playerdata).

## Getting PlayerData in your resource [#getting-playerdata-in-your-resource]

Load the imports file in your manifest. It fetches the `ESX` object, fills `ESX.PlayerData` on `esx:playerLoaded`, applies every `esx:setPlayerData` update coming from `es_extended`, and clears the data on logout.

```lua title="fxmanifest.lua"
shared_scripts {
    '@esx_lib/imports.lua',
    '@es_extended/imports.lua',
}
```

<Callout type="warn">
Since the `ESX` object comes from an export, your resource gets a **copy** of the table. If you only call `exports['es_extended']:getSharedObject()` once, `ESX.PlayerData` is a snapshot and does not update. Use the imports file, or `ESX.GetPlayerData()`, which always returns the current data from `es_extended`.
</Callout>

## Fields [#fields]

Values after `esx:playerLoaded`. Some keys are added later by ESX on the client.

| Key | Type | Description |
|---|---|---|
| `identifier` | `string` | Character identifier, for example `char1:abc...` with multicharacter. |
| `ssn` | `string` | Social security number of the character. |
| `name` | `string` | Character name (`firstName lastName` when esx_identity data exists, else the FiveM name). |
| `firstName`, `lastName` | `string` | Character names. |
| `dateofbirth`, `sex`, `height` | `string`, `string`, `number` | Identity data when available. |
| `group` | `string` | Permission group, for example `user` or `admin`. |
| `job` | `table` | `id`, `name`, `label`, `type`, `grade`, `grade_name`, `grade_label`, `grade_salary`, `skin_male`, `skin_female`. |
| `accounts` | `table[]` | Each `{ name, money, label, round, index }`. Use [ESX.GetAccount](/docs/es_extended/client/functions). |
| `money` | `number` | Cash at load time. Use `ESX.GetAccount('money')` for the current value. |
| `inventory` | `table[]` | Built-in inventory items (`name`, `count`, `label`, `weight`, `usable`, `rare`, `canRemove`). |
| `weight`, `maxWeight` | `number` | Current and maximum inventory weight. |
| `loadout` | `table[]` | Weapons: `name`, `label`, `ammo`, `components`, `tintIndex`. Built-in inventory only. |
| `metadata` | `table` | Character metadata (health, armor, jobDuty and custom keys). |
| `variables` | `table` | Values set with `xPlayer.set` (replicated). |
| `coords` | `vector3` | Live position of the ped, computed on read. |
| `ped` | `number` | Current player ped. Updated when the ped changes. |
| `dead` | `boolean` | `true` after death, `false` after spawn. |
| `weapon` | `number` | Hash of the selected weapon. |
| `vehicle` | `number \| false` | Vehicle the player is entering or sitting in. |
| `seat` | `number \| false` | Seat index in that vehicle. |

With `Config.CustomInventory` (ox_inventory), `inventory`, `weight` and `loadout` are not maintained by ESX. Use the inventory's own exports.

```lua title="client/main.lua"
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
    print(('Hello %s, you work as %s (%s)'):format(
        xPlayer.firstName,
        xPlayer.job.label,
        xPlayer.job.grade_label
    ))
end)
```

## Reacting to changes [#reacting-to-changes]

### OnPlayerData [#onplayerdata]

The imports file defines a global function `OnPlayerData(key, val, last)` that does nothing by default. Override it in your resource to react to every PlayerData update from `es_extended`.

| Name | Type | Description |
|---|---|---|
| `key` | `string` | Changed key, for example `job`, `accounts`, `dead`, `ped`. |
| `val` | `any` | New value. |
| `last` | `any` | Previous value. |

```lua title="client/main.lua"
function OnPlayerData(key, val, last)
    if key == 'job' and val.name ~= (last and last.name) then
        RefreshJobBlips(val.name)
    elseif key == 'dead' and val then
        CloseAllMyMenus()
    end
end
```

### esx:setPlayerData [#esxsetplayerdata]

`ESX.SetPlayerData` fires the local event `esx:setPlayerData` with `(key, val, last)`. Listen to it if you do not use the imports file. The imports file only accepts this event when it comes from `es_extended`, so other resources cannot fake updates into your copy.

```lua
AddEventHandler('esx:setPlayerData', function(key, val)
    if key == 'accounts' then
        UpdateHudMoney(val)
    end
end)
```

### Specific events [#specific-events]

For single values you can also listen to the secure net events that change them: `esx:setJob`, `esx:setAccountMoney`, `esx:setGroup`, `esx:setInventory`, `esx:setMaxWeight` and `esx:updatePlayerData`. See [Client events](/docs/es_extended/events/client).

## Common issues [#common-issues]

- **`ESX.PlayerData.job` is nil in a thread at resource start.** Wait for `ESX.IsPlayerLoaded()` first.
- **Changing `ESX.PlayerData` does nothing on the server.** It is a client cache. Change data on the server through xPlayer.
- **Money in `PlayerData.money` is outdated.** Read the `money` account instead.

## Related [#related]

- [Player data (server and database)](/docs/es_extended/playerdata)
- [Client functions](/docs/es_extended/client/functions)
- [Client events](/docs/es_extended/events/client)
- [xPlayer](/docs/es_extended/server/xplayer)
