esx_inventory
esx_inventory is the default ESX Legacy NUI inventory. Learn its config, keybinds, the server storage API exports and how it relates to ox_inventory.
esx_inventory is the default slot-based inventory UI for ESX Legacy. It opens with F2, shows items, money and weapons from es_extended, and offers a small server API so other resources can show a second "storage" panel (stashes, society stock, trunks). es_extended remains the authority on items and weight; this resource is the presentation layer.
Overview
| Fact | Value |
|---|---|
| Resource | esx_inventory |
| Side | Client UI (React NUI) + server storage bridge |
| Dependencies | es_extended, esx_lib |
| Database | None (slot layout is stored in client KVP esx_inventory:slots) |
| Version | 1.16.3 (shipped with ESX Legacy 1.16.4) |
Features verified in the source:
- Slot grid with drag and drop. The layout is saved per player with
SetResourceKvp. - Use, give to a nearby player and drop items, routed through the validated es_extended events (
esx:useItem,esx:giveInventoryItem,esx:removeInventoryItem). - Give the ammo of a selected weapon to a nearby player.
- Cash, black money and weapons shown next to items.
- Hotbar styling on the first slots with keybinds that use the item in that slot.
- Toasts for gained and lost items, even while the inventory is closed.
- Optional second panel for storages registered by other resources.
Not compatible with ox_inventory
es_extended checks GetResourceState("ox_inventory") ~= "missing" and sets Config.CustomInventory = "ox", which disables EnableDefaultInventory. esx_inventory then stops with ESX Default Inventory is disabled in config, please enable it to use this resource. If you use ox_inventory, do not ensure esx_inventory. See ox_inventory with ESX.
Installation
esx_inventory is part of the [core] folder. Start it after its dependencies:
ensure oxmysql
ensure esx_lib
ensure es_extended
ensure esx_inventoryThe UI ships prebuilt in web/dist. You only need to rebuild it if you change web/src:
cd esx_inventory/web
pnpm install
pnpm buildConfiguration
All keys live in config/main.lua.
| Key | Default | Description |
|---|---|---|
Locale | GetConvar("esx:locale", "en") | Language used for UI strings and notifications. |
PlayerSlots | 25 | Number of slots in the player grid. Slot indexes saved in KVP are clamped to this range. |
HotbarSlots | 5 | Number of first slots styled as hotbar. One keybind is registered per hotbar slot. |
ItemImageUrl | "nui://esx_inventory/web/images/%s.png" | Image URL template. %s is replaced with the item, account or weapon name. |
NearbyPlayerDistance | 3.0 | Max distance (meters) for a player to appear in the "give" target list. |
StorageBurst | 5 | Rate limiter capacity for storage put/take actions per player. |
StorageRefillInterval | 1000 | Interval (ms) after which the storage rate limiter refills StorageBurst tokens. |
Theme convars
The panel reads the shared ESX UI convars through xLib.colors.getESXTheme(). Set them with setr so the client can read them:
setr esx:ui:primaryColor "#fb9b04"
setr esx:ui:logoUrl "https://example.com/logo.png"Supported convars: esx:ui:primaryColor, esx:ui:secondaryColor, esx:ui:backgroundColor, esx:ui:accentColor, esx:ui:logoUrl.
Item images
Put itemname.png files (for example bread.png, WEAPON_PISTOL.png, money.png) into web/images/. Missing images fall back to an icon. To use a CDN, change ItemImageUrl to something like "https://cdn.example.com/items/%s.png".
Keybinds
Keybinds are registered with xLib.addKeybind, so players can rebind them in Settings > Key Bindings > FiveM.
| Name | Default key | Action |
|---|---|---|
showinv | F2 | Open or close the inventory. |
hotbar1 ... hotbar5 | 1 ... 5 | Use the item in hotbar slot 1-5. Weapons are equipped or holstered. |
While a hotbar key is held, the matching GTA weapon-wheel shortcut control is disabled so the key does not also switch weapons. The inventory does not open while the player is dead, the pause menu is active, or the player is not loaded yet.
Client exports
ShowInventory
exports.esx_inventory:ShowInventory()Opens the inventory for the local player. Does nothing if it is already open, the NUI is not ready, the player is dead or the pause menu is open.
RegisterCommand('inv', function()
exports.esx_inventory:ShowInventory()
end, false)Server exports (storage API)
A storage is a second panel next to the player inventory. The registering resource owns the data; esx_inventory handles the player side (remove before put, carry check before and after take, refund on failure) and runs one transaction at a time per player.
RegisterStorage
exports.esx_inventory:RegisterStorage(storageId, definition) -- returns boolean| Name | Type | Description |
|---|---|---|
storageId | string | Unique, non-empty storage id. |
definition.label | string | Title shown on the panel. Required. |
definition.canAccess | fun(xPlayer): boolean | Access check. Required. |
definition.getItems | fun(xPlayer): table[] | Returns { name, count, label?, weight? } entries. Required. |
definition.putItem | fun(xPlayer, itemName, count): boolean | Adds to your storage. Return true only after the state changed. Required. |
definition.takeItem | fun(xPlayer, itemName, count): boolean | Removes from your storage. Return true only after the state changed. Required. |
definition.slots | number? | Max distinct items. Default 30. |
definition.maxWeight | number? | Max total weight, 0 disables the check. Default 0. Weights come from the es_extended item registry. |
definition.coords | vector3? | Optional location. Transfers are refused when the player is too far away. |
definition.distance | number? | Allowed distance from coords. Default 5.0. |
Returns false when the id is invalid, a callback is missing, or another resource already owns the id. The owning resource may call it again to replace its definition; open viewers are refreshed. Only item_standard entries are supported in storages: money and weapons cannot be stored through this API.
Callbacks must not yield
The callbacks run synchronously in your resource. Do not call MySQL.query.await, Wait or other yielding functions inside them. Keep the storage contents in memory and persist them separately.
local stock = {} -- [itemName] = count, load it from your database on start
local function toList()
local list = {}
for name, count in pairs(stock) do
list[#list + 1] = { name = name, count = count }
end
return list
end
local function registerStorages()
exports.esx_inventory:RegisterStorage('society_mechanic', {
label = 'Mechanic Stock',
slots = 30,
maxWeight = 200,
coords = vector3(-342.29, -133.37, 39.0),
distance = 5.0,
canAccess = function(xPlayer)
return xPlayer.getJob().name == 'mechanic'
end,
getItems = function()
return toList()
end,
putItem = function(_, itemName, count)
stock[itemName] = (stock[itemName] or 0) + count
return true
end,
takeItem = function(_, itemName, count)
local current = stock[itemName] or 0
if current < count then return false end
stock[itemName] = current - count > 0 and current - count or nil
return true
end,
})
end
AddEventHandler('esx_inventory:ready', registerStorages)
if GetResourceState('esx_inventory') == 'started' then
registerStorages()
endOpenStorage
exports.esx_inventory:OpenStorage(playerId, storageId) -- returns boolean| Name | Type | Description |
|---|---|---|
playerId | number | Server id of the player. |
storageId | string | Id passed to RegisterStorage. |
Returns false if the storage does not exist, the player is not loaded, out of range, canAccess returns false, or getItems errors. The same function is also bound to the local server event esx_inventory:openStorage, so TriggerEvent('esx_inventory:openStorage', playerId, storageId) works from other server scripts.
RegisterNetEvent('mechanic:openStock', function()
local src = source
if not exports.esx_inventory:OpenStorage(src, 'society_mechanic') then
local xPlayer = ESX.GetPlayerFromId(src)
if xPlayer then xPlayer.showNotification('You cannot open this storage.') end
end
end)RefreshStorage
exports.esx_inventory:RefreshStorage(storageId) -- returns booleanPushes the current getItems result to everyone viewing the storage. Call it after you change the contents outside of putItem / takeItem (for example from an admin command). Viewers who lost access or moved out of range get the panel closed.
UnregisterStorage
exports.esx_inventory:UnregisterStorage(storageId) -- returns booleanRemoves a storage and closes it for every viewer. Storages are also unregistered automatically when the owning resource stops.
Events
| Event | Side | Type | Description |
|---|---|---|---|
esx_inventory:ready | Server | Local | Fired when esx_inventory starts. Register your storages again here. |
esx_inventory:openStorage | Server | Local | (playerId, storageId), same as OpenStorage. |
esx_inventory:storagePut | Server | Net (internal) | Sent by the UI. Rate limited and validated. |
esx_inventory:storageTake | Server | Net (internal) | Sent by the UI. Rate limited and validated. |
esx_inventory:storageClosed | Server | Net (internal) | Sent when the player closes the storage panel. |
esx_inventory:openStorage / refreshStorage / closeStorage | Client | Net (internal) | Sent by the server to drive the storage panel. |
Do not trigger the internal net events yourself; use the exports. The UI refreshes itself on the es_extended events esx:setInventory, esx:addInventoryItem, esx:removeInventoryItem, esx:addLoadoutItem, esx:removeLoadoutItem, esx:setAccountMoney and esx:playerLoaded, and closes on esx:onPlayerDeath.
Common issues
Config.CustomInventory is not false in es_extended. Most often ox_inventory exists in your resources folder (even if not started, the check is ~= "missing"). Remove it or stop using esx_inventory.
The hotbar keybinds default to 1-5 and suppress the weapon-wheel shortcut while held. Players can unbind or rebind them in the FiveM key binding settings.
Storages live in memory of esx_inventory. Register them in an esx_inventory:ready handler as shown above so they come back after a restart.
putItem/takeItem must return exactly true. Any other value, or an error, counts as failure and the player's items are refunded. Also check slots, maxWeight, coords/distance and the rate limit (StorageBurst actions per StorageRefillInterval).
Related
Frequently asked questions
Independent documentation
Last updated on
esx_identity
esx_identity lets ESX Legacy players register first name, last name, date of birth, sex and height. Config, events, commands and validation rules.
esx_menu_default
esx_menu_default reference for ESX Legacy. Build keyboard list menus with ESX.UI.Menu.Open, sliders, element fields, menu methods, keybinds and fixes.