# NUI basics: building interfaces for FiveM

> What FiveM NUI is and how it works: the embedded Chromium browser, ui_page and files in the fxmanifest, SendNUIMessage and the message event, cfx-nui asset URLs, focus and callbacks, loading screens versus ui_page, and choosing plain HTML or React/Vue/Svelte.

Source: https://fam-tools.com/guides/fivem-nui-basics · FAM Tools · published 2026-09-11 · updated 2026-09-11 · about 11 min

**Short answer.** 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`.

## Key points

- NUI = a web page per resource, drawn over the game by embedded Chromium.
- fxmanifest: `ui_page` + `files`.
- Lua → UI: `SendNUIMessage`; UI → Lua: NUI callbacks.
- Assets: `https://cfx-nui-<resource>/...` (the older `nui://` scheme is deprecated).
- The page is transparent — only what you draw covers the game.

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

*fxmanifest.lua*

```lua
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](https://fam-tools.com/guides/fivem-loading-screen-react-vite).

## Sending data to the page

*client.lua*

```lua
RegisterCommand('bank', function()
    SendNUIMessage({ action = 'open', balance = 12500 })
    SetNuiFocus(true, true)
end, false)
```

*html/app.js*

```js
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](https://fam-tools.com/guides/fivem-nui-callbacks-fetch). Keyboard and mouse control: [NUI focus and cursor](https://fam-tools.com/guides/fivem-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](https://fam-tools.com/guides/fivem-loading-screen-html-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.

> **Hide, do not just cover** — A hidden UI should be `display: none`, not transparent. Invisible elements still cost rendering time and can catch mouse clicks when focus is on.

## 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`.

## Related

- https://fam-tools.com/guides/fivem-registerkeymapping-nui
- https://fam-tools.com/guides/fivem-nui-svelte-vue-vite
- https://fam-tools.com/guides/fivem-nui-focus-and-cursor
- https://fam-tools.com/guides/fivem-nui-callbacks-fetch
- https://fam-tools.com/guides/fivem-nui-devtools-debugging
- https://fam-tools.com/guides/fivem-loading-screen-html-tutorial
