Tablet Apps (External UI)
Other resources can put their own UI inside the MDT tablet, so players carry one tablet instead of several. A registered app shows up in the MDT navigation like any other tab.
An app opens in one of two ways:
- Embedded (recommended): your resource's own NUI page is shown inside the MDT screen, under the MDT navigation. The MDT keeps the tablet prop, the animation and NUI focus.
- Hand-off: the MDT hides and your resource shows its own NUI. The tablet prop stays in the player's hand.
All exports on this page are client-side. Keep your own item or command as a fallback for servers that do not run kartik-mdt.
RegisterTabletApp
exports['kartik-mdt']:RegisterTabletApp(app)| Field | Type | Description |
|---|---|---|
id | string | Unique app id. Your resource name is a good choice. |
label | string | Name shown in the MDT navigation. |
icon | string? | An icon from the custom page icon set (Shield, Notebook, FolderOpen, ...) or an image URL. |
order | number? | Sort order among apps. Default 100. |
ui | string? | Embedded apps only: your NUI page, for example https://cfx-nui-my-resource/web/index.html. The MDT adds ?embedded=1 to it. Leave it out for a hand-off app. |
open | function(ctx) | Called when the player opens the app. ctx.embedded is true for embedded apps, ctx.fromTablet for hand-off apps. In both cases skip your own prop and animation, and do not call SetNuiFocus when embedded. |
close | function? | Called when the MDT closes while your app is open. |
canOpen | function? | Return false to hide the app for this player, for example a job check. |
Returns true when the app was registered.
Apps are removed automatically when your resource stops. Register on start, and again on kartik-mdt:tablet:ready in case the MDT starts after your resource:
local function registerApp()
if GetResourceState('kartik-mdt') ~= 'started' then return end
exports['kartik-mdt']:RegisterTabletApp({
id = GetCurrentResourceName(),
label = 'Evidence',
icon = 'FolderOpen',
ui = ('https://cfx-nui-%s/web/index.html'):format(GetCurrentResourceName()),
open = function(ctx) OpenMyTablet(ctx) end,
close = function() CloseMyTablet() end,
})
end
CreateThread(registerApp)
AddEventHandler('kartik-mdt:tablet:ready', registerApp)Embedded apps
SendNUIMessage from your resource only reaches your own NUI frame, not your page inside the MDT. While embedded:
-
Send messages to your page with
SendTabletAppMessage(id, payload).payloadarrives as a normalmessageevent, the same shape asSendNUIMessage. -
Messages are queued until your page is ready. Once your message listener is attached, post:
window.parent.postMessage({ tabletAppReady: true }, '*'); -
fetch('https://<your-resource>/<callback>')from your page still reaches your ownRegisterNUICallbacks. -
Inside the MDT
invokeNativemay be missing, so check for?embedded=1in the URL instead of treating the page as a normal browser. -
Do not call
SetNuiFocus, the MDT owns focus. To hide the tablet for a moment (a camera view, a screenshot), callSetTabletSuspended(true), thenSetTabletSuspended(false)to bring it back. -
When your page closes itself (for example on ESC), call
CloseMDT()to put the tablet away.
exports['kartik-mdt']:SendTabletAppMessage('my-resource', { action = 'refresh', data = records })
exports['kartik-mdt']:SetTabletSuspended(true)
exports['kartik-mdt']:CloseMDT()Hand-off apps
ReturnToTablet(): for your Home button. Hide your UI first, then call it. The MDT reopens with the tablet still in hand.CloseTabletApp(): your UI closed outright (ESC). The tablet prop is put away.GetActiveTabletApp(): returns the id of the hand-off app currently showing, ornil.
UnregisterTabletApp
exports['kartik-mdt']:UnregisterTabletApp(id)Removes an app. If it is open, it is closed.
Opening the MDT on your app
openMDTPage(path) opens the MDT (if it is closed) straight on a page. Use it with your app's id to open your app directly, for example from an item:
exports['kartik-mdt']:openMDTPage('/tablet-app?id=my-resource')