FiveM Docs
Guides

ESX vs QBCore

A neutral comparison of ESX Legacy and QBCore for FiveM servers covering architecture, data storage, API style, ecosystem, learning curve and migration.

ESX Legacy and QBCore are the two most widely used roleplay frameworks for FiveM. Both give you a persistent player object with money, a job and an inventory, both run on Lua and both use oxmysql for the database. The real differences are in how data is stored, how the API looks and which scripts already exist for each. This page compares them as neutrally as possible so you can choose, or understand what a migration involves.

Scope

This site documents ESX Legacy 1.16.4 and every ESX statement below is checked against its source code. For QBCore, read the official documentation at qbcore.net. QBCore changes over time, so verify QBCore specifics there before you make a decision.

Quick comparison

TopicESX LegacyQBCore
Core resourcees_extended plus esx_libqb-core
Getting the core objectexports['es_extended']:getSharedObject() or @es_extended/imports.luaexports['qb-core']:GetCoreObject()
Player objectxPlayer with methods like xPlayer.addAccountMoney()Player object with methods under Player.Functions
Jobs and gradesDatabase tables jobs and job_gradesDefined in shared Lua files by default
ItemsDatabase table items (or ox_inventory item files)Defined in shared Lua files by default
MoneyAccounts from Config.Accounts: money, bank, black_moneyMoney types configured in QBCore (cash, bank and others)
Database driveroxmysql (hard dependency in fxmanifest.lua)oxmysql
OneSyncRequired (OneSync Infinity)See QBCore docs
LicenseGPL-3.0 (SPDX header in every ESX file)See the QBCore repository

Architecture

ESX Legacy

ESX Legacy is split into a core and optional resources:

  • es_extended is the framework core. It loads players, manages accounts, jobs, groups, the default inventory, commands, callbacks and paychecks.
  • esx_lib is a shared library that es_extended depends on. It provides callbacks, table and math helpers, points, streaming helpers and the SQL catalog that creates and seeds tables on startup.
  • Core resources like esx_multicharacter, esx_identity, esx_skin, esx_context and esx_notify handle character creation and UI.
  • Addons like esx_policejob, esx_society, esx_billing and esx_garage add gameplay.

The design is database-first: jobs, grades and items live in SQL tables. You can add a job with a single INSERT and the /refreshjobs command, or at runtime with ESX.CreateJob(). See Create a job and Add items.

ESX refuses to start without OneSync Infinity: the join handler rejects players with "ESX Requires Onesync Infinity to work" when the onesync convar is off or legacy.

QBCore

QBCore is built around the qb-core resource and a family of qb-* resources. By default, static data like jobs, items, gangs and vehicles is defined in Lua files inside qb-core/shared, and player data is stored in a players table. Check qbcore.net for the current file layout, because it has changed between versions.

What this means in practice

You want to...ESX LegacyQBCore
Add a jobINSERT into jobs and job_grades, then /refreshjobsEdit the shared jobs file and restart
Add an itemINSERT into items, then /refreshitems (or edit ox_inventory items)Edit the shared items file (or the inventory resource)
Change data without a restartSupported for jobs and items through the refresh commandsDepends on the version and resource
Edit data from a web panelEasy, it is all SQLRequires editing Lua files

Database-first is convenient for admin panels and live changes. File-first is convenient for version control and code review. Neither is wrong.

API style

Both frameworks expose the same concepts with different names. Here is the same task, giving a player money and reading their job, in ESX:

server/main.lua
local ESX = exports['es_extended']:getSharedObject()

RegisterCommand('payday', function(source)
    local xPlayer = ESX.GetPlayerFromId(source)
    if not xPlayer then return end

    xPlayer.addAccountMoney('bank', 500, 'Payday command')

    local job = xPlayer.getJob()
    xPlayer.showNotification(('Paid as %s (%s)'):format(job.label, job.grade_label))
end, false)

In QBCore the equivalent uses the QBCore player object and its Functions table. If you are porting a script, map each call one by one:

ConceptESX Legacy (verified)Notes
Get playerESX.GetPlayerFromId(source)Returns nil when the player is not loaded
Add moneyxPlayer.addAccountMoney(account, amount, reason)addMoney() is a shortcut for the money account
Remove moneyxPlayer.removeAccountMoney(account, amount, reason)Returns false if the balance would go negative
Set jobxPlayer.setJob(name, grade, onDuty)Grade must exist in job_grades
Check itemxPlayer.getInventoryItem(name) / xPlayer.hasItem(name)Proxied to ox_inventory when it is running
Server callbackESX.RegisterServerCallback(name, function(source, cb, ...) end)See Callbacks
Player loaded eventesx:playerLoadedServer: (playerId, xPlayer, isNew)

ESX also replicates some player data through state bags: Player(source).state.job, .group and .name are set by es_extended, which helps when you write framework-agnostic code. See Performance.

Ecosystem

ESX has been around since 2017, so a very large number of free and paid scripts target it, including many written for old ESX versions. Most of those still run on ESX Legacy because the classic API (ESX.GetPlayerFromId, xPlayer.addMoney, esx:playerLoaded) is kept. Old scripts usually need small fixes, covered in Convert old ESX scripts.

QBCore has its own large ecosystem of qb-* and third-party resources. Many commercial script authors ship both an ESX and a QBCore version, or a single script with a framework bridge.

The overextended resources (oxmysql, ox_lib, ox_inventory, ox_target) work with both frameworks. ESX Legacy detects ox_inventory automatically. See ox_inventory with ESX.

Do not mix frameworks

es_extended contains a resource guard that stops qb-core (and old resources like essentialmode and es_admin2) when it detects them and prints "WE STOPPED A RESOURCE THAT WILL BREAK ESX". Pick one framework per server.

Performance

You will find many claims online that one framework is "faster". We do not repeat any numbers here because we know of no neutral, reproducible benchmark. In practice:

  • An idle framework core costs very little. What hurts servers is scripts with Wait(0) loops, unindexed queries and chatty events.
  • Both frameworks can run large servers when the installed scripts are well written.
  • Measure your own server with resmon and the FiveM profiler instead of trusting comparisons.

ESX Legacy has some built-in measures, for example paychecks are paid in batches (Config.PaycheckBatchSize, Config.PaycheckBatchDelay, Config.PaycheckJitter) and players are saved every 10 minutes in one background thread. Details are in Performance.

Learning curve

  • ESX has the most tutorials and forum answers of any FiveM framework, but many of them are outdated (esx:getSharedObject events, mysql-async). Learning to recognize old patterns is part of learning ESX. The core API is small and consistent: one ESX object, one xPlayer object.
  • QBCore documentation and tutorials are mostly newer. If you are used to its Functions naming, ESX will look different but conceptually similar.

If your team already knows one framework, that is usually the strongest argument for it.

Migrating between frameworks

There is no official converter in ESX Legacy for QBCore data. A migration is a project, not a setting. Plan it like this:

Inventory your scripts

List every resource and check whether an ESX version exists. Scripts that only exist for the other framework must be rewritten or replaced.

Map your data

Player money, jobs, inventories and vehicles are stored in different tables and formats. In ESX, player data is in users (accounts and inventory are JSON in accounts and inventory) and vehicles in owned_vehicles. See the Database schema.

Write and test a one-off migration

Write a script or SQL that converts the data, and run it on a copy of the database. Check identifiers: ESX stores the license without the license: prefix, and with esx_multicharacter as char1:<license>.

Run both servers side by side

Keep the old server running until the new one is tested with real players. Never run both frameworks in one server.

How to decide

  • Choose ESX Legacy if you want database-driven jobs and items, need a specific ESX script, or your developers already know ESX.
  • Choose QBCore if the scripts you need exist only for QBCore or your team already knows it. Start at qbcore.net.
  • If you are undecided, list the 10 scripts your server cannot live without and check which framework they support.

Common issues

  • "WE STOPPED A RESOURCE THAT WILL BREAK ESX": you have qb-core or another conflicting resource in your server.cfg. Remove it.
  • A QBCore script throws attempt to index a nil value (global 'QBCore') on an ESX server: the script needs a QBCore core. Use its ESX version or a bridge.
  • Players kicked with "ESX Requires Onesync Infinity": set onesync on (txAdmin does this for you) before starting ESX.

Frequently asked questions

Independent documentation

esx-fivem.com is a community project and is not affiliated with the ESX Framework team. Facts on this page were checked against the upstream source. Official resources: docs.esx-framework.org and github.com/esx-framework.
Edit on GitHub

Last updated on

On this page