Custom apps
Build your own apps and integrate them directly into QuanticPhone. Custom apps appear in the home screen grid and behave just like native apps.
Overview
A custom app is a separate FiveM resource that registers itself with QuanticPhone at runtime. It has its own NUI (HTML/JS/React) that renders inside the phone frame, and communicates with the phone and your server using standard FiveM NUI callbacks and events.
File structure
A minimal custom app resource looks like this:
my-custom-app/
├── fxmanifest.lua
├── client.lua
├── server.lua (optional)
└── web/
├── index.html
├── app.js (or your bundled frontend)
└── icon.png (512×512 recommended)fxmanifest.lua
fx_version "cerulean"
game "gta5"
author "Your Name"
description "My custom phone app"
version "1.0.0"
client_script "client.lua"
server_script "server.lua" -- optional
ui_page "web/index.html"
files {
"web/index.html",
"web/app.js",
"web/icon.png",
}Register the app
There are two ways to register a custom app:
Option 1: File-based (Recommended)
Place your app files in one of these directories within the quanticPhone resource:
server/custom/apps/your-app/client/custom/apps/your-app/shared/custom/apps/your-app/
Create a manifest.json file in your app directory:
{
"id": "my-custom-app",
"name": "My App",
"description": "Short app description",
"icon": "nui://my-custom-app/web/icon.png",
"ui": "nui://my-custom-app/web/index.html",
"category": "utilities",
"size": "10MB",
"version": "1.0.0"
}Option 2: Programmatic (Server-side)
From a server-side script, call RegisterCustomApp():
-- In your resource's server script
RegisterCustomApp({
id = "my-custom-app",
name = "My App",
description = "Short app description",
icon = "nui://my-custom-app/web/icon.png",
ui = "nui://my-custom-app/web/index.html",
category = "utilities",
})id must be unique across all registered apps. Use your resource name as a prefix to avoid conflicts.NUI communication
Use standard FiveM NUI callbacks to communicate between your frontend and Lua:
-- client.lua
RegisterNUICallback("myAction", function(data, cb)
-- data comes from your frontend (JS fetch)
print("Received from NUI:", json.encode(data))
cb({ ok = true }) -- always call cb to release the NUI thread
end)
-- Send data TO the NUI (frontend)
SendNUIMessage({
type = "UPDATE_DATA",
payload = { balance = 5000 },
})// app.js — inside your frontend
async function sendToLua(action, data) {
const res = await fetch(`https://my-custom-app/${action}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
return res.json();
}
// Listen for messages from Lua
window.addEventListener("message", (event) => {
if (event.data.type === "UPDATE_DATA") {
console.log("Balance:", event.data.payload.balance);
}
});fetch to call other origins from inside the NUI. All backend communication must go through NUI callbacks (Lua → server via events).Add to server.cfg
Start your app after QuanticPhone so the export is available at registration time.
ensure quanticPhone
ensure my-custom-app