NUI basics: building interfaces for FiveM
Declare a page with ui_page and list its files under files in the fxmanifest. Lua sends data with SendNUIMessage({...}), which arrives in the page as a message event with the table in event.data. The page talks back through NUI callbacks (fetch to https://<resource>/<name>), and SetNuiFocus gives it keyboard and mouse. Files are addressed as https://cfx-nui-<resource>/path.
Every modern FiveM interface — inventories, phones, HUDs, menus, loading screens — is a web page. FiveM embeds a Chromium browser (CEF) on top of the game, and each resource can show its own page there. That page is called NUI. If you can write HTML, CSS and JavaScript, you can build FiveM UI.
Declaring the page
fx_version 'cerulean'
game 'gta5'
ui_page 'html/index.html'
files {
'html/index.html',
'html/style.css',
'html/app.js',
'html/img/*.png',
}
client_script 'client.lua'Every file the page loads must be listed in files, or it is not sent to players and the browser gets a 404. Wildcards work. A built React/Vite app is listed the same way — see React and Vite.
Sending data to the page
RegisterCommand('bank', function()
SendNUIMessage({ action = 'open', balance = 12500 })
SetNuiFocus(true, true)
end, false)window.addEventListener('message', (event) => {
const msg = event.data;
if (msg.action === 'open') {
document.querySelector('#balance').textContent = '$' + msg.balance;
document.body.classList.add('open');
}
});The Lua table is converted to JSON and arrives as a plain JavaScript object. Use one field (like action) to route messages.
Talking back to Lua
The page answers with a POST request to its resource; Lua handles it with RegisterNuiCallback. Full details and a reusable helper: NUI callbacks with fetch. Keyboard and mouse control: NUI focus and cursor.
Asset URLs
| Reference | Use |
|---|---|
img/logo.png (relative) | Files in your own resource |
https://cfx-nui-ox_inventory/web/images/water.png | Files from another resource |
nui://resource/path | Older scheme, deprecated — prefer cfx-nui |
https://… | External hosting (CDN, remote images) |
NUI page vs loading screen
| `ui_page` | `loadscreen` | |
|---|---|---|
| Shown | In game, whenever you display it | While the player joins |
| Talks to Lua | Messages and callbacks | Only receives loading events and handover data |
| Focus | SetNuiFocus | loadscreen_cursor for the mouse |
Loading screens are covered in the HTML loading screen tutorial.
Plain HTML or a framework?
Plain HTML and JavaScript are perfect for small UIs. For inventories, phones or anything with lots of state, a framework (React, Vue, Svelte) built to static files keeps the code manageable. Either way the result is static files in files — nothing runs on a Node server.
Common questions
What is NUI in FiveM?
The in-game web layer: each resource can show an HTML page, rendered by embedded Chromium over the game.
Why does my NUI page not show?
Common causes: ui_page path wrong, files missing from files, the resource not restarted, or the page hidden by CSS. Check the devtools console.
How do I send data from Lua to NUI?
Call SendNUIMessage({...}) and listen for the message event in the page; the table arrives as event.data.
Can I use React for FiveM NUI?
Yes. Build it to static files, point ui_page at the built index.html and list the build output in files.
How do I load an image from another resource?
Use https://cfx-nui-<resource>/<path> — the file must be in that resource’s files.
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