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).ACE Permissions
You can restrict app visibility using FiveM ACE permissions. When enabled, only players with the correct permission can see and install the app.
-- In your server.cfg
add_ace group.admin quanticphone.app.my-custom-app allow
add_ace identifier.steam:xxxxx quanticphone.app.my-custom-app allow
-- Or restrict to a specific ACE group
add_ace group.vip quanticphone.app.premium-app allowTo enable ACE permission checks, set in shared/Config.lua:
Config.ACE = {
Enabled = true, -- Enable ACE permission checks
DefaultPermission = true, -- Apps without permission visible to all
Prefix = "quanticphone.app", -- ACE permission prefix
}DefaultPermission = true, apps without a specific ACE permission are visible to everyone. Set to false to require explicit permission for all apps.Add to server.cfg
Start your app after QuanticPhone so the export is available at registration time.
ensure quanticPhone
ensure my-custom-app