Frameworks
Making your script work with every common inventory
Integrate FiveM scripts with inventories: ox_inventory exports (AddItem, RemoveItem, GetItemCount, CanCarryItem), qb-inventory exports (AddItem, RemoveItem, GetItemCount, HasItem), default ESX inventory via xPlayer, detecting which is running, a small inventory bridge, usable items, and item images.
Overview
Almost every script eventually gives, takes or checks items, and every inventory does it differently. A thin inventory layer in your resource — add, remove, count, can carry — keeps the rest of the code identical whether the server runs ox_inventory, qb-inventory or ESX’s default inventory.
The APIs side by side
| Operation | ox_inventory | qb-inventory | ESX default |
|---|---|---|---|
| Add | AddItem(src, name, n, meta) | AddItem(src, name, n, slot, info, reason) | xPlayer.addInventoryItem(name, n) |
| Remove | RemoveItem(src, name, n, meta, slot) | RemoveItem(src, name, n, slot, reason) | xPlayer.removeInventoryItem(name, n) |
| Count | GetItemCount(src, name) | GetItemCount(src, name) | xPlayer.getInventoryItem(name).count |
| Can carry | CanCarryItem(src, name, n) | CanAddItem(src, name, n) | xPlayer.canCarryItem(name, n) |
A small inventory bridge
Inv = {}
local kind = GetResourceState('ox_inventory') == 'started' and 'ox'
or GetResourceState('qb-inventory') == 'started' and 'qb'
or 'esx'
function Inv.count(src, name)
if kind == 'ox' then return exports.ox_inventory:GetItemCount(src, name) end
if kind == 'qb' then return exports['qb-inventory']:GetItemCount(src, name) or 0 end
local x = ESX.GetPlayerFromId(src)
local item = x and x.getInventoryItem(name)
return item and item.count or 0
end
function Inv.remove(src, name, n)
if kind == 'ox' then return exports.ox_inventory:RemoveItem(src, name, n) end
if kind == 'qb' then return exports['qb-inventory']:RemoveItem(src, name, n, false, 'script') end
local x = ESX.GetPlayerFromId(src)
if not x or x.getInventoryItem(name).count < n then return false end
x.removeInventoryItem(name, n)
return true
end
function Inv.add(src, name, n, meta)
if kind == 'ox' then return exports.ox_inventory:AddItem(src, name, n, meta) end
if kind == 'qb' then return exports['qb-inventory']:AddItem(src, name, n, false, meta, 'script') end
local x = ESX.GetPlayerFromId(src)
if not x then return false end
x.addInventoryItem(name, n)
return true
endPair it with a framework bridge — see framework bridges. ox_inventory officially supports ox_core, ESX, Qbox and ND_Core; on QBCore, qb-inventory is the default.
Usable items
| Inventory | How |
|---|---|
| ox_inventory | client.export/server.export on the item in items.lua — see ox_inventory items |
| QBCore / qb-inventory | QBCore.Functions.CreateUseableItem(name, fn) |
| ESX | ESX.RegisterUsableItem(name, fn) |
Rules that prevent dupes
- Remove the cost first and reward only if the removal returned success.
- Check carry capacity before adding, or handle the failure.
- All item operations happen on the server — see secure server events.
- Use inventory hooks for global rules — see ox_inventory hooks.
Frequently asked questions
How do I add an item with ox_inventory from my script?
On the server: exports.ox_inventory:AddItem(source, 'itemname', count, metadata).
How do I support ox_inventory and qb-inventory at once?
Detect which is started with GetResourceState and route add/remove/count through a small bridge module.
How do I count items in ESX without ox_inventory?
xPlayer.getInventoryItem('name').count.
How do I prevent item duplication?
Do everything on the server and only reward after a successful removal.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- FrameworksSupporting several frameworks with a bridgeDetect the running framework with GetResourceState('qbx_core'), ('qb-core') and ('es_extended'), load a matching bridge file, and have every bridge expose the same functions — get player identifier, job, money add/remove, notify, item add/remove. Keep framework calls out of your main logic, allow a config override, and test on each framework you claim to support.
- EngineeringWriting server events that cannot be abusedAny client can call any event you registered with RegisterNetEvent, with any arguments. In each handler: copy source into a local, check argument types and ranges, re-derive everything from server state (prices, amounts, rewards), verify the player can do this now (distance, job, item, cooldown), and log refusals. Events only other server scripts should use are registered with AddEventHandler alone, so clients cannot trigger them.
- FrameworksWriting scripts for Qbox (qbx_core)On the server call qbx_core exports: exports.qbx_core:GetPlayer(source), AddMoney(source, 'cash', amount, reason), SetJob(source, job, grade), HasGroup(source, filter) and Notify(source, text, type). On the client, add '@qbx_core/modules/playerdata.lua' to get QBX.PlayerData, kept in sync automatically. Items and inventories go through ox_inventory; UI through ox_lib. A bridge keeps most QBCore scripts working.
- FrameworksWriting your first ESX Legacy scriptImport ESX with shared_script '@es_extended/imports.lua' (or exports.es_extended:getSharedObject()). On the server, ESX.GetPlayerFromId(source) returns xPlayer, which handles money (addMoney, addAccountMoney('bank', …)), items (addInventoryItem, getInventoryItem), and the job (xPlayer.job.name, .grade). Register usable items with ESX.RegisterUsableItem and commands with ESX.RegisterCommand. On the client use ESX.PlayerData and the esx:playerLoaded and esx:setJob events.