House X Studios — Core Library
A lightweight, framework-agnostic core library for FiveM resources. hx-lib provides a single unified API (hx.*) on top of QBox, QBCore, and ESX, plus batteries-included modules for callbacks, logging, localization, map entities (blips, peds, markers), and a bundled NUI component kit.
Features
-
Multi-framework bridge — auto-detects QBox, QBCore, or ESX at runtime; your code calls one API regardless of the active framework.
-
Client ↔ server callbacks — promise-free callback system with automatic timeout cleanup.
-
Leveled logger — colored console output with
debug/info/warn/errorlevels, runtime-adjustable. -
Localization — JSON locale files with
printf-style formatting and automatic English fallback. -
Map entity managers — create and auto-clean blips, peds, and interactive markers; everything is tracked and removed on resource stop.
-
Bundled NUI kit — alerts, context menus, notifications, dialogs, skill checks, interaction prompts, action bars, list menus, and radial menus, with two themes.
-
Self update check — compares the running version against the latest GitHub release.
-
Drop-in imports — pull the library into any resource with a single
@hx-lib/imports/*.lualine.
Requirements
-
A recent FiveM artifacts build (Lua 5.4 support required).
-
One of the supported frameworks installed and started:
Framework
Detected resource
Inventory used
QBox
qbx_core
ox_inventory
QBCore
qb-core
framework default
ESX
es_extended
ESX built-in
hx-libworks without a framework loaded, buthx.Bridge.*calls will be unavailable and a warning is logged on start.
Installation
-
Drop the
hx-libfolder into your serverresourcesdirectory. -
Start
hx-libbefore any resource that depends on it. Add it high in yourserver.cfg, immediately after your framework:ensure qbx_core # or qb-core / es_extended ensure ox_inventory # if using QBox ensure hx-lib -
Import the library in your own resource (see Quick Start).
Framework Support
The active framework is detected automatically by checking resource state, in this order: QBox → QBCore → ESX.
hx.Bridge.GetFramework() --> 'qbox' | 'qbcore' | 'esx' | nil
hx.Bridge.Is('qbox') --> boolean
All hx.Bridge.* functions below present the same signature on every framework, so your resource code never branches on framework type.
Quick Start
Add the imports you need to your resource's fxmanifest.lua. These lines load hx-lib's modules into your resource's Lua state, exposing the global hx table.
-- your_resource/fxmanifest.lua
shared_scripts {
'@hx-lib/imports/shared.lua',
}
client_scripts {
'@hx-lib/imports/client.lua',
'client/main.lua',
}
server_scripts {
'@hx-lib/imports/server.lua',
'server/main.lua',
}
Then use it anywhere:
-- server/main.lua
RegisterCommand('paycheck', function(source)
local name = hx.Bridge.GetName(source)
hx.Bridge.AddMoney(source, 'bank', 500, 'paycheck')
hx.Bridge.Notify(source, ('%s received $500'):format(name), 'success')
hx.Log.Info('myresource', 'Paid %s', name)
end)
-- client/main.lua
local blip = hx.Blip.Create({
coords = vec3(441.0, -982.0, 30.6),
sprite = 60, color = 29, scale = 0.9,
label = 'Police Station',
})
API Reference
Availability legend: server · client · shared
hx.Bridge — framework abstraction
**Server **
Function
Returns
Description
GetPlayer(source)
player object
Raw framework player object.
GetIdentifier(source)
string|nil
Citizen ID / license identifier.
GetName(source)
string
"Firstname Lastname" (or "Unknown").
GetJob(source)
table|nil
Normalized job (see below).
GetMoney(source, type)
number
type is 'cash', 'bank', etc.
AddMoney(source, type, amount, reason)
boolean
RemoveMoney(source, type, amount, reason)
boolean
Notify(source, msg, type?, duration?)
—
type defaults to 'inform'.
AddItem(source, item, count, metadata?)
boolean
metadata honored on QBox (ox_inventory).
RemoveItem(source, item, count, metadata?)
boolean
HasItem(source, item, count?)
boolean
count defaults to 1.
GetInventory(source)
table
Normalized job shape returned by GetJob:
{
name = 'police',
label = 'Police',
grade = 3, -- numeric level
gradeName = 'sergeant',
onDuty = true,
isBoss = false,
}
**Client **
Function
Returns
Description
GetPlayerData()
table
Raw framework player data.
GetJob()
table|nil
Same normalized shape as server.
Notify(msg, type?, duration?)
—
Local notification.
-- Server
if hx.Bridge.HasItem(source, 'lockpick', 1) then
hx.Bridge.RemoveItem(source, 'lockpick', 1)
hx.Bridge.AddMoney(source, 'cash', 250, 'sold-loot')
end
-- Client
local job = hx.Bridge.GetJob()
if job and job.name == 'police' and job.onDuty then
hx.Bridge.Notify('Welcome on duty, officer.', 'success')
end
Money types: ESX maps
'cash'to its'money'account internally, so use'cash'/'bank'everywhere for portability.
hx.Callback — client/server callbacks
Request data from the server and get the result in a callback. Pending requests time out after Config.CallbackTimeout (default 10s) and are cleaned up automatically.
-- Server: register
hx.Callback.Register('myresource:getBalance', function(source, account)
return hx.Bridge.GetMoney(source, account)
end)
-- Client: trigger
hx.Callback.Trigger('myresource:getBalance', function(balance)
print('Bank balance:', balance)
end, 'bank')
Side
Function
Description
Register(name, handler)
handler(source, ...) returns the value sent back to the client. Errors are caught and logged; the client receives nil.
Trigger(name, cb, ...)
Sends a request; cb(result) fires when the server responds.
hx.Log **— leveled logger **
hx.Log.Debug('myresource', 'value = %s', someValue)
hx.Log.Info('myresource', 'Started')
hx.Log.Warn('myresource', 'Low on %d items', count)
hx.Log.Error('myresource', 'Failed: %s', err)
hx.Log.SetLevel('debug') -- runtime override
-
Levels:
debug(1) →info(2) →warn(3) →error(4). Messages belowConfig.LogLevelare suppressed. -
Output format:
[hx] [LEVEL] [resource] message, color-coded by level. -
Messages are formatted with
string.format(safely — bad args won't error).
hx.Utils **— helpers **
Function
Signature
Description
Trim(s)
string
Strip leading/trailing whitespace.
Split(s, sep)
table
Split string by separator.
Round(num, decimals?)
number
Round to N decimals (default 0).
DeepCopy(orig)
table
Recursive table copy.
Contains(tbl, val)
boolean
Value membership test.
TableLength(tbl)
integer
Count keys (non-sequential safe).
Format(str, ...)
string
string.format that returns the raw string instead of erroring on bad args.
RandomString(length?)
string
Alphanumeric, default length 8.
Merge(t1, t2)
table
Shallow merge; t2 overrides t1.
hx.Locale **— localization **
hx.Locale.T('welcome_message', playerName) -- formatted, with args
hx.Locale.SetLocale('en')
hx.Locale.GetLocale() --> 'tr'
-
Loads
locales/.jsonon demand. Ships withtr(default) anden. -
Missing key → falls back to English → falls back to the key itself.
-
Supports
string.formatsubstitution when args are passed.
hx.Version **— update checker **
hx.Version.Check('my-shop', '1.2.0', 'https://github.com/hx-studios/hx-shop')
Queries the GitHub latest release API and logs whether your resource is current or outdated. Controlled by Config.VersionCheck. hx-lib runs this against itself on startup.
hx.Blip **— client **
local id = hx.Blip.Create({
coords = vec3(441.0, -982.0, 30.6),
sprite = 60,
color = 29,
scale = 0.9, -- optional, default 0.8
label = 'Police',
shortRange = true, -- optional, default true
})
hx.Blip.Remove(id)
hx.Blip.RemoveAll()
hx.Blip.GetAll() --> { blipHandle, ... }
All blips are tracked and removed automatically when the resource stops.
hx.Ped **— client **
local ped = hx.Ped.Create({
model = 's_m_y_cop_01',
coords = vec4(441.0, -982.0, 30.6, 90.0),
freeze = true, -- default true
invincible = true, -- default true
blockEvents = true, -- default true
scenario = 'WORLD_HUMAN_GUARD_STAND', -- OR:
anim = { dict = 'amb@world_human_...', name = 'idle_a' },
})
hx.Ped.Remove(ped)
hx.Ped.RemoveAll()
hx.Ped.GetAll()
Validates and streams the model with a timeout, applies sensible defaults (frozen, invincible, no ambient AI), then releases the model. Tracked and auto-removed on resource stop.
hx.Marker **— client **
Draws a marker and fires interaction callbacks based on player distance. The render loop sleeps when no marker is nearby.
local id = hx.Marker.Create({
coords = vec3(441.0, -982.0, 30.6),
type = 1, -- marker type, default 1
scale = vec3(1.0, 1.0, 1.0),
color = { r = 255, g = 0, b = 0, a = 100 },
rotate = false,
bobUpAndDown = false,
distance = 10.0, -- draw distance
interactDistance = 1.5, -- enter/inside trigger range
onEnter = function(id, marker) end,
onInside = function(id, marker) end, -- every frame while inside
onExit = function(id, marker) end,
})
hx.Marker.Remove(id)
hx.Marker.RemoveAll()
hx.UI — bundled NUI
A complete NUI component kit rendered by hx-lib's own UI page (web/index.html). Blocking components return their result via Citizen.Await, so you can call them synchronously inside a thread. Two themes are available: v1 (sharp) and v2 (rounded, default — set in Config.UI.Theme).
Important:
hx.UIis not exposed through@hx-lib/imports/client.lua. The NUI page is owned by thehx-libresource, sohx.UI.*runs insidehx-lib's own client context (see the bundled/hx:uitest commands). External resources that need this UI should drive it fromhx-librather than callinghx.UIdirectly. See Notes & Caveats.
Function
Blocking
Returns
Alert(data)
yes
boolean confirmed
Context(data)
yes
number|nil 1-based index
Menu(data)
yes
number|nil 1-based index
Radial(data)
yes
number|nil 1-based index
Dialog(data)
yes
table|nil field values
SkillCheck(data)
yes
boolean success
Notify(data)
no
—
ShowInteract(data) / HideInteract()
no
—
ShowActionBar(data) / HideActionBar()
no
—
CloseContext() / CloseMenu() / CloseRadial()
no
—
SetTheme('v1'|'v2')
no
—
-- Confirm dialog
if hx.UI.Alert({ title = 'Delete?', description = 'This cannot be undone.' }) then
-- confirmed
end
-- Input form
local values = hx.UI.Dialog({
title = 'Register Vehicle',
fields = {
{ type = 'text', name = 'plate', label = 'Plate' },
{ type = 'number', name = 'price', label = 'Price', min = 0, max = 100000 },
},
})
if values then print(values.plate, values.price) end
-- Notification (fire-and-forget)
hx.UI.Notify({ title = 'Saved', message = 'Vehicle stored.', type = 'success' })
-- Skill check
local ok = hx.UI.SkillCheck({ difficulty = 'medium', current = 1, total = 3 })
Built-in test commands (registered by modules/client/ui_test.lua): run /hx:ui in the F8 console for the full list — /hx:alert, /hx:context, /hx:dialog, /hx:menu, /hx:radial, /hx:skillcheck, /hx:notify, /hx:interact, /hx:actionbar, /hx:theme.
Configuration
Edit shared/config.lua:
hx.Config = {
Debug = false, -- debug mode
LogLevel = 'info', -- 'debug' | 'info' | 'warn' | 'error'
DefaultLocale = 'tr', -- default language
VersionCheck = true, -- enable GitHub version check
NotifyDuration = 5000, -- default notification duration (ms)
CallbackTimeout = 10000, -- callback timeout (ms)
Brand = {
Name = 'hx Studios',
Color = '^5', -- console color code (cyan)
Prefix = '[hx]',
},
UI = {
Theme = 'v2', -- 'v1' (sharp) | 'v2' (rounded)
},
}
Localization
Locale files live in locales/ as flat JSON key→string maps:
{
"lib_loaded": "Library loaded | Framework: %s | v%s",
"version_outdated": "Outdated! Current: %s, Latest: %s"
}
%s / %d placeholders are filled by the args passed to hx.Locale.T(key, ...). Add a new language by dropping locales/.json and setting Config.DefaultLocale (or calling hx.Locale.SetLocale).
Exports
-- Server: get the full hx table from another resource
local hx = exports['hx-lib']:Gethx()
This export is server-side only. On the client, access
hxvia the global created by@hx-lib/imports/client.lua.
Project Structure
hx-lib/
├── fxmanifest.lua
├── init.lua # global hx table + namespaces + framework state
├── imports/
│ ├── shared.lua # @hx-lib/imports/shared.lua
│ ├── client.lua # @hx-lib/imports/client.lua
│ └── server.lua # @hx-lib/imports/server.lua
├── shared/
│ ├── config.lua
│ ├── utils.lua
│ └── locale.lua
├── bridge/
│ ├── loader.lua # framework detection
│ ├── client/{qbox,qbcore,esx}.lua
│ └── server/{qbox,qbcore,esx}.lua
├── modules/
│ ├── client/{logger,callback,blip,ped,marker,ui,ui_test}.lua
│ └── server/{logger,callback,version}.lua
├── client/main.lua # ready flag + cleanup on stop
├── server/main.lua # ready flag + self version check + Gethx export
├── locales/{tr,en}.json
└── web/ # NUI (index.html, css, js)
Notes & Caveats
-
Load order matters.
hx-libmust start before dependent resources, and your framework (andox_inventoryfor QBox) must start beforehx-lib. -
hx.UIimport gap.imports/client.lualoadslogger,callback,blip,ped, andmarker— but notui. The UI module is only loaded inhx-lib's own client state (viafxmanifestmodules/client/*.lua), because the NUI page belongs tohx-lib. External client scripts including the import get every module excepthx.UI. -
Test commands ship enabled.
modules/client/ui_test.luaregisters/hx:*commands and is loaded unconditionally (not gated behindConfig.Debug). Remove or gate it for production. -
Inventory backends differ. QBox uses
ox_inventory(metadata supported); ESX uses its built-in inventory (no metadata param). Keep item calls portable by not relying on metadata unless you target QBox.
License
© House X Studios. All rights reserved unless a license file states otherwise.
Preview
Added As Secondary Media