Converting an ESX Script to QBox
Converting an ESX script to QBox is one of the most common tasks for a server owner modernizing their stack. The good news: most conversions are mechanical. ESX and QBox model the same things a player object, money accounts, jobs, and items they just expose them through different functions. Once you have a mapping between the two, the bulk of the work is find-and-replace plus a few structural changes around inventory, which QBox delegates to ox_inventory.
This guide gives you that mapping and a clear order of operations: how to replace getSharedObject, how xPlayer becomes the qbx_core player object, how money and job calls translate, and how to move inventory logic to ox_inventory. It is written for a real conversion, not a toy example. PlayDeck teaches conversions with AI: you paste in the ESX script, the AI proposes the QBox equivalent, and you steer it verifying each mapped call instead of trusting a blind auto-convert, which is how you avoid the subtle money and inventory bugs that bite careless migrations.
Step 1: Replace the framework object
ESX scripts open by grabbing the shared object, usually with ESX = exports['es_extended']:getSharedObject() (or, in old code, a deprecated esx:getSharedObject event). QBox favors direct exports and has no single shared-object line you must set up. Instead, wherever the ESX script later did ESX.GetPlayerFromId(source), you'll call exports.qbx_core:GetPlayer(source) at the point of use.
So the first pass is to delete the getSharedObject setup and rewrite each player lookup. ESX.GetPlayerFromId(source) becomes local player = exports.qbx_core:GetPlayer(source). Keep the nil check both frameworks return nil for an invalid or not-yet-loaded source, and guarding it is just as important after the conversion.
Step 2: Map the player object and money
ESX's xPlayer becomes QBox's player object, and the money functions translate directly. xPlayer.addMoney(1000) becomes player.Functions.AddMoney('cash', 1000). xPlayer.removeMoney(1000) becomes player.Functions.RemoveMoney('cash', 1000, 'reason'). ESX account money, like xPlayer.addAccountMoney('bank', 500), becomes player.Functions.AddMoney('bank', 500, 'reason'). Reading a balance xPlayer.getAccount('bank').money becomes player.PlayerData.money.bank.
Identity fields move too: ESX's identifier maps to player.PlayerData.citizenid, the permanent per-character ID in QBox. One thing to decide deliberately during conversion is how 'dirty' or black money is represented, since ESX's black_money account doesn't map one-to-one you'll choose whether it becomes a cash variant, a bank account, or an item, and the right answer depends on how the original script used it.
- ESX.GetPlayerFromId(src) → exports.qbx_core:GetPlayer(src)
- xPlayer.addMoney(n) → player.Functions.AddMoney('cash', n)
- xPlayer.removeMoney(n) → player.Functions.RemoveMoney('cash', n, 'reason')
- xPlayer.getAccount('bank').money → player.PlayerData.money.bank
- ESX identifier → player.PlayerData.citizenid
Step 3: Translate jobs and events
Jobs follow the same pattern. ESX's xPlayer.setJob('police', 2) becomes player.Functions.SetJob('police', 2). Reading the job ESX's xPlayer.getJob().name becomes player.PlayerData.job.name. Job-grade checks translate field-for-field, so job-locked features usually convert with no logic changes, just renamed accessors.
Events differ in name but not in concept. ESX's esx:playerLoaded becomes QBox's player-loaded lifecycle event, and esx:setJob becomes the QBox job-update event. The client-server event mechanics (RegisterNetEvent, TriggerServerEvent, TriggerClientEvent) are identical across frameworks because they're part of FiveM itself, not ESX or QBox so your event wiring carries over unchanged; only the framework-specific event names you listen for need updating.
Step 4: Move inventory to ox_inventory
This is the step that takes the most thought. ESX scripts often call ESX inventory functions or assume a particular inventory; QBox uses ox_inventory by default, which is slot-based and exposes its own exports. ESX's xPlayer.addInventoryItem('water', 1) becomes exports.ox_inventory:AddItem(source, 'water', 1), and item checks become exports.ox_inventory:GetItemCount or a search export.
Because ox_inventory has its own item definitions and metadata model, you may need to register the script's items in ox_inventory's data and adjust any code that assumed ESX's item structure. If the original script implemented its own storage UI, you can often drop it and use ox_inventory's stash system instead, which is cleaner and more secure. This is where blindly auto-converting fails inventory is the part that genuinely needs a human (or a well-steered AI) to think through.
Step 5: Fix start order and test
QBox depends on the ox stack, so resource start order matters: oxmysql, then ox_lib, then qbx_core and ox_inventory, then your converted resource. Declare these dependencies in your fxmanifest.lua so the server starts them in the right sequence; a wrong order produces nil exports that look like conversion bugs but aren't.
Finally, test the converted script against a real flow, not just startup. Buy and sell, earn and spend money, change jobs, and confirm balances and items persist across a reconnect. Watch the server console for nil errors, which almost always point at a missed mapping or a wrong start order. A careful, function-by-function conversion with real testing is far more reliable than any one-click converter and it's exactly the kind of methodical work AI accelerates while you stay in the driver's seat.
Frequently asked questions
Can I just run my ESX script on QBox unchanged?
No. QBox is backwards compatible with many QBCore scripts, but not with ESX. ESX scripts use a different player object and function names, so they must be converted. QBCore-to-QBox is the migration that's often near-automatic; ESX-to-QBox is a real conversion.
What's the trickiest part of an ESX to QBox conversion?
Inventory. QBox uses ox_inventory, which is slot-based with its own item definitions and metadata. Money and jobs are mostly find-and-replace, but inventory logic and any custom storage UI usually need genuine rework.
How does ESX black money convert to QBox?
There's no one-to-one mapping. Decide based on how the script used it: it might become a cash variant, a bank-style account, or a marked-bills item in ox_inventory. Choosing the wrong representation is a common source of post-conversion bugs.
Is it legal to convert and sell a converted script?
Only if the original code is yours or licensed for it. Converting someone else's paid ESX script and reselling it is piracy. Convert your own original work, sell it on Tebex with a Cfx.re license, and keep everything above board.