Keeping FiveM NUI fast
Send NUI messages only when values change, and throttle continuous data (speed, fuel) to a few updates per second. Hide closed UIs with display: none, animate only transform and opacity, avoid large blurs and shadows, keep images the size they are displayed at, and stop React from re-rendering the whole tree on every message. Measure the Lua side in resmon and the page in the devtools Performance tab.
A HUD that costs 2 FPS is a HUD every player pays for, all session long. NUI cost has two halves: the Lua side sending data (visible in resmon) and the browser side rendering it (visible as lower FPS and in the devtools). Both are easy to keep small once you know the patterns.
Send less, less often
local last = {}
CreateThread(function()
while true do
local sleep = 500
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
if veh ~= 0 then
sleep = 150
local speed = math.floor(GetEntitySpeed(veh) * 3.6)
local fuel = math.floor(GetVehicleFuelLevel(veh))
if speed ~= last.speed or fuel ~= last.fuel then
last.speed, last.fuel = speed, fuel
SendNUIMessage({ action = 'vehicle', speed = speed, fuel = fuel })
end
elseif last.speed then
last = {}
SendNUIMessage({ action = 'vehicle', hide = true })
end
Wait(sleep)
end
end)Each message is encoded to JSON and handed to the browser process. Rounding values and sending only changes turns hundreds of messages per second into a handful.
Cheap CSS
| Expensive | Cheaper |
|---|---|
Animating width, top, left | transform: translate/scale |
backdrop-filter, big filter: blur() | Semi-transparent solid backgrounds |
Many large, soft box-shadows | One small shadow or a border |
Hidden with opacity: 0 | display: none when closed |
| Huge PNGs scaled down in CSS | Images exported at display size, WebP |
React and framework UIs
- Store fast-changing values (speed, health) in small components, not in a top-level state that re-renders everything.
- Use
React.memo/ selectors so a speed update does not re-render the inventory grid. - Unmount closed screens instead of rendering them invisibly.
- Build for production — development builds are much slower.
Measuring
Resmon shows the Lua cost of your client thread (aim for 0.00–0.02 ms idle). Rendering cost does not appear there: record a few seconds in the devtools Performance tab and compare in-game FPS with the UI open and closed. See NUI devtools and resmon explained.
Loading screens
Loading screens compete with the game loading itself. The same rules apply, plus file size — see loading screen performance.
Common questions
Why does my HUD lower FPS?
Usually messages sent every frame, heavy CSS effects, or a framework re-rendering everything on each update.
How often should a HUD update?
Only when values change, and at most around 4–10 times per second for continuous values like speed.
Does resmon show NUI rendering cost?
No. Resmon shows the Lua side; use in-game FPS and the devtools Performance tab for rendering.
Should I hide UI with opacity or display none?
display: none for closed UIs — invisible elements still cost rendering.
Want a loading screen you never have to debug?
Build it in the browser, export once, and edit it whenever — no HTML, no re-uploads.
Start free