Assets & UI
Patterns for talking between NUI and Lua
Proven patterns for FiveM NUI communication: the action/data message convention, NUI callbacks with RegisterNuiCallback and fetch, request/response flows through the server, pushing state vs pulling it, throttling updates, focus handling, and validating everything the UI sends.
Overview
Every NUI resource has the same plumbing: Lua sends data to the page, the page asks Lua to do things, and anything that matters goes on to the server. Getting that plumbing right — one message convention, one callback helper, validation on the server — keeps complex UIs like phones and inventories maintainable.
The request/response flow
RegisterNuiCallback('bank:transfer', function(data, cb)
if type(data.to) ~= 'number' or type(data.amount) ~= 'number' then
return cb({ ok = false, error = 'invalid' })
end
local result = lib.callback.await('bank:transfer', false, data.to, data.amount)
cb(result)
end)lib.callback.register('bank:transfer', function(source, to, amount)
-- validate target, amount limits, balance; then move money
return { ok = true }
end)The page sees one fetch that resolves with the server’s answer. Details: NUI callbacks and callbacks explained.
Push vs pull
| Data | Pattern |
|---|---|
| Opening a menu | Push everything the UI needs in one open message |
| Balance, job, inventory changes | Push when it changes (framework events, state bags) |
| Speed, fuel, health | Poll a few times per second in Lua; send on change |
| Rarely needed details | Pull with a callback when the user opens them |
Focus and lifecycle
SetNuiFocus(true, true)on open;SetNuiFocus(false, false)on every close path and on resource stop.- A
closecallback from the page (Escape key) so Lua can release focus. - Details: NUI focus and cursor.
Structuring bigger UIs
Type message payloads once and share them between Lua docs and the frontend; keep the Lua layer thin (validation and forwarding) and put business rules on the server. A React structure that follows these patterns: React NUI boilerplate.
Security
Players can call NUI callbacks from the devtools with any data. Treat callback input like any client input: validate types in Lua and re-check everything on the server — see secure server events.
Frequently asked questions
How should NUI and Lua exchange data?
Lua sends { action, data } messages with SendNUIMessage; the page calls Lua through NUI callbacks with fetch.
How does NUI get data from the server?
The NUI callback in Lua calls a server callback (for example lib.callback.await) and returns the result with cb.
Should my HUD poll values every frame?
No — poll a few times per second in Lua and send only changes.
Are NUI callbacks secure?
No more than any client input; validate on the server.
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
- FrameworksCallbacks: asking the server a question and getting an answerA callback registers a named handler on one side and lets the other side call it and receive its return value. With ox_lib use lib.callback.register on the server and lib.callback.await on the client (it also works server → client). ESX uses ESX.RegisterServerCallback / ESX.TriggerServerCallback, QBCore QBCore.Functions.CreateCallback / QBCore.Functions.TriggerCallback. Validate inside callbacks exactly like events.
- 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.
- Assets & UIFiveM NUI developmentCEF quirks, the Lua↔JS contract, React/Next.js builds, NUI focus, and the CSS that silently fails inside the game.
- EngineeringOptimising FiveM Lua scriptsSleep loops dynamically (long Wait when nothing is nearby, Wait(0) only when you must draw or read input), replace polling with events and state bags, measure distance with #(a - b) on vectors, read values like the player ped once per tick (or from ox_lib’s cache), use backtick hashes, avoid creating tables and strings in hot loops, and batch database writes. Measure before and after with resmon and the profiler.