# Create Custom App

## Creating a Custom App

This guide walks you through creating a custom app that runs inside the 0r-smarttab tablet.

{% embed url="<https://github.com/0resmon/0r-customapp-templates>" %}

***

### Step 1: Register the App

Open the admin panel in-game:

```
/tabadmin
```

Click **"Add New App"** and fill in the form:

| Field           | Description                             | Example                                         |
| --------------- | --------------------------------------- | ----------------------------------------------- |
| **App Name**    | Display name on the tablet              | `Stock Market`                                  |
| **App ID**      | Unique identifier (no spaces)           | `app-stocks`                                    |
| **Description** | Short description (optional)            | `Real-time stock tracker`                       |
| **Category**    | Dropdown: General, Job, Services, etc.  | `Services`                                      |
| **Color**       | Pick a color for the icon background    | *(click a circle)*                              |
| **Icon Path**   | Path to icon image                      | `/appicons/stocks.png`                          |
| **Iframe URL**  | Your app's NUI URL                      | `https://cfx-nui-my-stocks/web/dist/index.html` |
| **Job Filter**  | Comma-separated jobs (empty = everyone) | `police, ems`                                   |

Click **"Create App"**. The app appears on all tablets immediately.

> **Icon tip:** Place your `.png` icon file in `0r-smarttab/web/dist/appicons/`. Use a square image (128x128 recommended).

***

### Step 2: Set Up the Resource

Download the **0r-customapp** template from GitHub. This is a ready-to-use FiveM resource with React + TypeScript.

#### File Structure

```
my-custom-app/
├── fxmanifest.lua
├── client.lua
├── server.lua
└── web/
    ├── src/
    │   ├── main.tsx
    │   ├── App.tsx
    │   └── hooks/
    │       └── useTabletContext.ts
    └── dist/
        └── index.html
```

#### fxmanifest.lua

```lua
fx_version 'cerulean'
game 'gta5'
lua54 'yes'

description 'My Custom App'
version '1.0.0'

client_scripts { 'client.lua' }
server_scripts { 'server.lua' }

ui_page 'web/dist/index.html'

files {
    'web/dist/index.html',
    'web/dist/**/*.*'
}
```

#### Iframe URL Format

The URL you entered in the admin panel must point to your resource:

```
https://cfx-nui-my-custom-app/web/dist/index.html
```

Replace `my-custom-app` with your resource folder name.

***

### Step 3: Receive Tablet Context

When your app opens inside the tablet, it automatically receives context data via `postMessage`. Use the included hook:

```tsx
import { useTabletContext } from './hooks/useTabletContext'

function App() {
  const ctx = useTabletContext()

  if (!ctx) return null // Not inside tablet yet

  return (
    <div>
      <p>Serial: {ctx.serial}</p>
      <p>Player Job: {ctx.playerJob}</p>
      <p>Language: {ctx.language}</p>
    </div>
  )
}
```

#### Context Fields

| Field       | Type     | Description                              |
| ----------- | -------- | ---------------------------------------- |
| `serial`    | `string` | Tablet serial number (unique per device) |
| `appId`     | `string` | App action ID (e.g. `app-stocks`)        |
| `appFullId` | `string` | Full app ID from apps.json               |
| `playerJob` | `string` | Player's current job name                |
| `language`  | `string` | Tablet language code (`en`, `tr`, etc.)  |
| `config`    | `object` | Server config values                     |

***

### Step 4: Use Tablet Features

Custom apps can use tablet notifications and badges through **Lua exports**. No JavaScript needed for these — just call them from your `client.lua` or `server.lua`.

#### Send a Notification

Shows a notification banner on the tablet (with sound, peek when closed, notification history).

**From client.lua:**

```lua
exports['0r-smarttab']:SendNotification({
    title    = 'Stock Market',
    description = 'BTC is up 12% today!',
    app      = 'app-stocks',   -- your app ID (for icon & peek)
    duration = 5000             -- ms, default 4000
})
```

**From server.lua:**

```lua
-- Send to a specific player
exports['0r-smarttab']:SendNotification(source, {
    title       = 'Stock Market',
    description = 'Your order has been filled.',
    app         = 'app-stocks',
    duration    = 5000
})
```

#### Set App Badge

Shows a red badge (number) on your app's icon on the home screen.

**From client.lua:**

```lua
-- Set badge to 3
exports['0r-smarttab']:SetAppBadge('app-stocks', 3)

-- Clear badge
exports['0r-smarttab']:SetAppBadge('app-stocks', 0)
```

**From server.lua:**

```lua
exports['0r-smarttab']:SetAppBadge(source, 'app-stocks', 5)
```

***

### Step 5: Listen for Tablet Events (Optional)

Your app's JavaScript can listen for theme and visibility changes from the tablet:

```js
window.addEventListener('message', (event) => {
  const { type, payload } = event.data || {}

  if (type === '0r-smarttab-theme-change') {
    // payload.theme = 'dark' or 'light'
    document.body.className = payload.theme
  }

  if (type === '0r-smarttab-visibility') {
    // payload.visible = true (tablet opened) or false (tablet closed)
  }
})
```

Your app can also request to close itself:

```js
window.parent.postMessage({ type: '0r-smarttab-close' }, '*')
```

***

### Quick Reference

#### Lua Exports

| Export              | Side   | Signature                                                     |
| ------------------- | ------ | ------------------------------------------------------------- |
| `SendNotification`  | Client | `exports['0r-smarttab']:SendNotification(data)`               |
| `SendNotification`  | Server | `exports['0r-smarttab']:SendNotification(targetSrc, data)`    |
| `SetAppBadge`       | Client | `exports['0r-smarttab']:SetAppBadge(appId, count)`            |
| `SetAppBadge`       | Server | `exports['0r-smarttab']:SetAppBadge(targetSrc, appId, count)` |
| `OpenTabletWithApp` | Client | `exports['0r-smarttab']:OpenTabletWithApp(appId)`             |

#### Iframe Messages (JS)

| Message                    | Direction    | Payload                                          |
| -------------------------- | ------------ | ------------------------------------------------ |
| `0r-smarttab-context`      | Tablet → App | `{ serial, appId, playerJob, language, config }` |
| `0r-smarttab-theme-change` | Tablet → App | `{ theme: 'dark' \| 'light' }`                   |
| `0r-smarttab-visibility`   | Tablet → App | `{ visible: boolean }`                           |
| `0r-smarttab-close`        | App → Tablet | *(no payload)*                                   |

#### Notification Data Fields

| Field         | Type     | Required | Default |
| ------------- | -------- | -------- | ------- |
| `title`       | `string` | Yes      | —       |
| `description` | `string` | Yes      | —       |
| `app`         | `string` | No       | —       |
| `icon`        | `string` | No       | —       |
| `duration`    | `number` | No       | `4000`  |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.0resmon.org/0resmon/0r-resources/0r-smarttab/create-custom-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
