NUI callbacks: sending data from the UI to Lua
In Lua, RegisterNuiCallback('name', function(data, cb) ... cb(result) end). In the page, fetch(https://${GetParentResourceName()}/name, { method: 'POST', body: JSON.stringify(data) }) and read the JSON response. Always call cb, even on errors, or the request never resolves. Treat callback data like any client input: validate on the server before anything that matters.
Messages go from Lua to the page; callbacks go the other way. When a player clicks “Buy”, the page makes a POST request to its own resource, Lua receives the data, does its work and answers — and the page gets that answer back as the fetch response.

#Registering the callback
RegisterNuiCallback('buyItem', function(data, cb)
if type(data.item) ~= 'string' then
cb({ ok = false, error = 'bad item' })
return
end
local ok = lib.callback.await('shop:buy', false, data.item, data.amount)
cb({ ok = ok })
end)RegisterNuiCallback exists in Lua, JavaScript and C#; Lua also accepts the older spelling RegisterNUICallback. The data arrives already parsed from JSON.
#Calling it from the page
async function buy(item, amount) {
const res = await fetch(`https://${GetParentResourceName()}/buyItem`, {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
body: JSON.stringify({ item, amount }),
});
const result = await res.json();
if (!result.ok) showError(result.error || 'Purchase failed');
}#A reusable helper with browser mocks
In a normal browser (while developing) GetParentResourceName does not exist. A helper that detects the game and returns mock data lets you build the UI in Chrome with hot reload.
const isGame = () => typeof (window as any).GetParentResourceName === 'function';
export async function fetchNui<T = unknown>(name: string, data: unknown = {}, mock?: T): Promise<T> {
if (!isGame()) return mock as T;
const res = await fetch(`https://${(window as any).GetParentResourceName()}/${name}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
body: JSON.stringify(data),
});
return res.json();
}
// usage
const result = await fetchNui<{ ok: boolean }>('buyItem', { item: 'water', amount: 2 }, { ok: true });#JavaScript and C# client scripts
RegisterNuiCallback('buyItem', (data, cb) => {
if (typeof data.item !== 'string') return cb({ ok: false });
emitNet('shop:buy', data.item, data.amount);
cb({ ok: true });
});#Callbacks are client input
#Common problems
| Symptom | Cause |
|---|---|
| Request stays pending forever | cb never called on some code path |
| Request fails immediately | Wrong resource name in the URL, or no callback registered under that name |
GetParentResourceName is not defined | Running in a normal browser — use the mock helper |
Unexpected end of JSON input | The page calls res.json() but cb returned nothing JSON-like — return a table or string |
Questions
How do I send data from NUI to Lua?
https://${GetParentResourceName()}/<name> from the page and handle it with RegisterNuiCallback in Lua.Why does my NUI fetch never finish?
cb. Every path, including errors, must call it.Can I return data to the page?
cb is JSON-encoded and becomes the fetch response.Are NUI callbacks secure?
Ready to pick a map?
Twelve themes on three base map styles, $8 each, instant download.
Relevant to what you just read