Starter templates
Quasar Smartphone includes starter templates to help developers create custom applications faster.
Available templates:
These templates already include the recommended structure for:
Use these templates if you want to build your own phone application without starting from zero.
Custom app iframe integration
Custom apps are external UIs that run inside the smartphone using an iframe.
This allows you to create apps using:
Custom apps must be registered from the client side of your resource, after qs-smartphone has fully started.
Registering a custom app
Basic example:
Custom app fields
| Field | Type | Description |
|---|---|---|
id | string | Unique app identifier. Use lowercase and avoid spaces. |
label | string | Name displayed to the player. |
icon | string | App icon URL. Usually points to your NUI build folder. |
category | string | App Store category. Example: Utilities, Games, Business. |
creator | string | Developer or studio name. |
description | string | Short App Store description. |
appStoreOnly | boolean | If true, the app appears only in the App Store. If false, it is pre-installed. |
price | number | Optional App Store price. If greater than 0, the player must buy it. |
sizeMb | number | Visual download size shown in the App Store. |
iframe.url | string | URL of your custom app HTML. |
custom.enabled | boolean | Enables custom app behavior. |
custom.bridge.enabled | boolean | Enables the JavaScript bridge. |
custom.bridge.allowedOrigins | table | Allowed NUI origins that can communicate with the phone. |
App Store price
You can make a custom app free or paid.
Free app:
Paid app:
You can also update the price later:
This is useful for dynamic prices, premium DLCs, temporary discounts or roleplay economy systems.
Custom app exports
These exports are used to manage custom apps dynamically.
| Export | Description |
|---|---|
addCustomApp(payload) | Registers one custom app. Returns success, errorCode. |
addCustomAppsBatch(payloads) | Registers multiple custom apps at once. |
updateCustomApp(appId, patch) | Updates specific fields of an existing app. |
removeCustomApp(appId) | Removes a custom app by its app ID. |
getCustomApps() | Returns the list of registered custom apps. |
Register multiple apps
Update an app
Only the fields included in the patch are updated.
Remove an app
Get all custom apps
Automatic app removal
Custom apps are automatically removed when the owner resource stops.
Example:
If my-custom-phone-app stops, its registered apps are removed automatically.
Built-in native smartphone apps are excluded from automatic removal, such as:
and other internal phone applications.
Push notifications server side
You can send lock-screen or banner notifications directly to a player phone.
This is useful for:
To send a scoped notification, the player must have an active phone scope.
Send notification using phone scope
Notification payload
| Field | Type | Description |
|---|---|---|
appId | string | App identifier related to the notification. |
appName | string | App display name. |
title | string | Main notification title. |
subtitle | string | Optional subtitle. |
text | string | Notification body text. |
closeTimeout | number | Time in milliseconds before closing automatically. |
Send notification using player server ID
You can also send a notification directly using the player server ID:
This is simpler when you already have the player source.
Notification return values
Notification exports return:
Common errors:
| Error | Meaning |
|---|---|
INVALID_SOURCE | The player source is invalid. |
NO_SCOPE | The player does not have an active phone scope. |
INVALID_PAYLOAD | The notification data is missing or invalid. |
Example:
Phone number and scope server exports
These exports allow you to get the player phone identity.
Get phone scope identifier
The scope ID is a persistent identifier used for:
Use this when your system needs to communicate with the correct phone instance.
Get current phone number
This returns the active SIM or metadata phone number for the player.
Use it when you need to:
Client helpers
Client helpers are exports used from your client-side Lua files.
Check if the phone is open
Returns true if the phone UI is currently visible.
Returns false if the phone is closed.
Example:
This is useful when you want to:
Start a call
You can start an audio or video call from your client resource.
Call types
Available call types:
Example video call:
Custom caller number
callerNumberOverride is optional.
Use it when your script needs to display a custom caller number.
Example:
This can be useful for:
Open a phone app
You can open a specific phone app while the phone is visible.
This export returns false if:
Example:
Use this when you want to redirect players to a specific app from another script.
Examples:
Housing battery charger qs-housing
If you use qs-housing, you can register or remove phone battery charger points inside houses.
This allows players to charge their phone inside custom housing locations.
Register a housing charger
Parameters:
| Parameter | Description |
|---|---|
houseId | Unique house identifier. |
vector3(x, y, z) | Charger position inside the house. |
2.0 | Interaction distance. |
Example:
Remove a housing charger
Example:
Use this when:
Iframe JavaScript bridge
The JavaScript bridge allows your custom iframe app to communicate with the smartphone.
It gives your app access to phone features such as:
Load the bridge SDK
Add this script inside your custom app HTML:
Create the bridge connection
| Field | Description |
|---|---|
appId | Your custom app ID. Must match the Lua registered app ID. |
targetOrigin | The phone NUI origin. Usually https://cfx-nui-qs-smartphone. |
Wait until the bridge is ready
Always wait for the bridge to be ready before using phone API methods.
Get current phone state
Example returned state:
Use this to know:
Get current phone language
Example result:
Other examples:
Use this to make your app adapt to the player phone language.
Translate phone text
Example:
This is useful if your app wants to reuse phone translations or stay consistent with the global smartphone language system.
Show toast notification
Use this for small app notifications inside the phone UI.
Open text prompt
This opens a text input prompt inside the phone.
Example:
Open option picker
Use this when the player needs to choose between multiple options.
Pick gallery media
Example filters:
This lets the player select media from the phone gallery.
Pick camera media
This opens the Camera app, allows the player to capture a photo, and returns the full gallery row.
Example returned data:
If the player cancels, it returns:
Pick GIF
Use this when your app needs GIF selection support.
Recorder API
Start recorder:
Stop recorder:
Use this for custom apps that need voice notes, reports, audio logs or immersive roleplay recordings.
Open another phone app from iframe
Example:
Close current phone app
Use this when your custom app needs to exit itself or return the player to the phone interface.
Get theme mode
This allows your iframe app to detect if the phone is using light or dark mode.
Use it to adapt your custom app style automatically.
Common bridge API methods
| Method | Description |
|---|---|
getPhoneState() | Returns current phone state. |
getPhoneLocale() | Returns current phone language. |
openPhoneApp(appId) | Opens another phone app. |
closeCurrentPhoneApp() | Closes the current app. |
getThemeMode() | Returns current theme mode. |
translateText(key) | Returns translated text from a locale key. |
showToastNotification(payload) | Shows a phone toast notification. |
openTextPrompt(payload) | Opens a text input prompt. |
openOptionPicker(payload) | Opens an option selector. |
pickGalleryMedia(payload) | Opens gallery picker. |
pickCameraMedia() | Opens camera capture flow. |
pickGif() | Opens GIF picker. |
startRecorder() | Starts audio recording. |
stopRecorder() | Stops audio recording and returns data. |
Template usage
Both starter templates use the same bridge API.
Example:
This means you can build your app in React or jQuery and still use the same smartphone bridge methods.
Custom bridge events
You can use bridge events to communicate between your iframe UI and your Lua resource.
This is useful for advanced integrations where your custom app needs to send actions to your game logic.
Emit a custom event
Listen for custom events
Example:
Built-in bridge events
The phone can send built-in events to your iframe app.
| Event | Payload | Description |
|---|---|---|
phone.theme.changed | `{ mode: 'light' | 'dark', darkMode: boolean }` |
phone.locale.changed | { language: string } | Triggered when the player changes phone language in Settings. |
app:opened | App data | Triggered when an app is opened. |
app:closed | App data | Triggered when an app is closed. |
Theme change example
Locale change example
Client events
You can listen to these events inside your own client resource.
| Event | Use |
|---|---|
phone:pushNotification | Shows a phone notification using the same payload as server push. |
phone:notification | Shows a simple ox_lib or qs-interface toast using msg and type. |
phone:usable:open | Triggered when the player uses the phone item. Useful if you need to open the UI manually. |
phone:incomingCall | Triggered when the player receives an incoming call. |
phone:callState | Triggered when the call session state changes. |
Trigger a phone notification without exports
You can also trigger a client notification directly from Lua:
Listen to incoming call
Listen to call state updates
NUI callback custom app to game
When your custom iframe app needs to communicate with your Lua resource, use standard FiveM NUI callbacks.
Register the callback inside your own client resource:
From React, Vue, jQuery or plain JavaScript, use the standard FiveM fetchNui pattern targeting your own resource name.
The recommended template already includes an example:
This is the recommended way to connect your custom app interface with Lua actions.
Recommended custom app structure
Example resource structure:
Example fxmanifest.lua:
Best practices
Use unique app IDs
Good:
Bad:
Avoid using IDs that may conflict with built-in phone apps.
Register apps after qs-smartphone starts
Your resource should start after qs-smartphone.
Example server.cfg:
Always check return values
Use allowedOrigins correctly
Your allowedOrigins should match your resource NUI origin:
If the origin is wrong, the bridge may not communicate correctly.
Keep icons optimized
Recommended icon format:
Recommended usage:
Use square icons with a clean design for the best App Store appearance.
Use the bridge for phone features
Do not recreate phone systems manually if the bridge already provides them.
Use:
instead of building separate duplicated systems.
Quick example: complete custom app flow
Example client.lua
Example index.html
Toggle Disabled
Enables or disables the smartphone completely, preventing players from opening or interacting with it. You can also check the current disabled state at any time.
Disable smartphone
Enable smartphone
Check disabled state
Send New Message From App
Sends a message directly to the Messages app as if it were sent by a system application or server script. The export automatically creates (or reuses) a conversation using the provided app name and can optionally display a push notification to the player.
Full Configuration
Parameters
You can target a player using either their server ID or phone number.
or
App label
Defines the conversation name shown inside the Messages app.
Message
The text content that will be delivered to the player.
Require online
Determines whether the player must be connected to receive the message.
Player must be online or the message will not be sent.
Message is saved and delivered even if the player is offline.
Push notification
Displays a notification when the message arrives.
Disable push notification
Send the message silently without showing a notification.
Push metadata
Attach custom data that can be used by integrations or custom applications.
Examples
Bank notification
Government message
Silent message
Offline delivery
Marketplace Shop Duty
Control and query the Marketplace app duty system directly from server-side scripts. This system manages the Enter Service and Exit Service actions available inside the Marketplace Profile tab.
Unlike the Settings Job Duty toggle (on_duty), Marketplace Duty is linked to the player's active Marketplace session and determines whether they are available as staff inside the Marketplace application.
All exports automatically resolve jobs configured in Config.Marketplace.Shops, so you only need to provide the framework job name. All exports are server-side and use the resource name qs-smartphone.
Check if a job exists
Verify whether a framework job is configured as a Marketplace shop.
Return value
Returns true if the job exists inside Config.Marketplace.Shops.
Parameters
Framework job name configured in the Marketplace.
Job names are case-insensitive.
All values are treated as the same job.
Check if a player is on duty
Determine whether a player is currently working in any Marketplace shop.
Return value
Returns true when the player is currently on duty.
Parameters
Server ID of the target player.
Get player's active duty job
Retrieve the Marketplace job currently assigned to a player.
Return value
Returns the framework job name while on duty.
Returns nil if the player is not currently on duty.
Parameters
Server ID of the target player.
Set player duty status
Clock a player into a Marketplace shop or remove them from duty.
Start duty
Stop duty
Return value
Returns true when the operation succeeds.
Parameters
Server ID of the target player.
Framework job name configured in Marketplace.
If omitted, the player will be removed from duty.
Check if a job has active staff
Determine whether at least one employee is currently on duty for a specific Marketplace job.
Return value
Returns true when at least one staff member is currently on duty.
Parameters
Framework job name configured in Marketplace.
