RM-SCRIPTS · FiveM NUI library — dialogue, mission HUD, timers, progress bars, notifications & TextUI.
Store · Documentation · Discord
rm-lib is a client NUI library for QBCore / Qbox-style servers: Dialogue, Mission Status, Timer (timed objectives), Progress (manual 0–100 bar and optional ox-compatible duration bar), Notifications (top-right toasts), and TextUI. Frontend: React + Mantine. Backend: Lua exports. Full usage details are in this README; product-specific guides may also appear on the RM-SCRIPTS documentation.
- ox_lib —
rm-libloads@ox_lib/init.luaas a shared script so you getcache,lib(ox), and stable player state viacache.ped. Ensureox_libstarts beforerm-lib(the manifest declaresdependency 'ox_lib').
-
Place
rm-libin your resources folder. -
Ensure
ox_libis installed and started. -
Add to
server.cfg(order matters):ensure ox_lib ensure rm-lib
-
Build the web UI if you change the React app:
cd web pnpm install pnpm build
Internally, dialogue camera / player alpha uses lib.getPlayerPed() from init.lua, which prefers cache.ped when ox_lib is present instead of calling PlayerPedId() repeatedly. You can use the same helper from other client scripts:
local ped = exports['rm-lib']:getPlayerPed()Interactive dialogue with NPC name, text, and options. Captures NUI focus.
| Export | Parameters | Description |
|---|---|---|
dialogue |
data: table, cb?: function |
Opens dialogue |
closeDialogue |
— | Closes programmatically |
isDialogueOpen |
— | boolean |
getDialogue |
— | Current data or nil |
Optional data.ped — NPC entity for scripted camera (see module).
exports['rm-lib']:dialogue({
name = 'Officer Davis',
text = 'We need your help with a case. Are you in?',
options = {
{ id = 'accept', label = "Yes, I'm in" },
{ id = 'decline', label = 'Not right now' },
}
}, function(optionId)
if optionId == nil then return end
if optionId == 'accept' then
-- ...
end
end)exports['rm-lib']:closeDialogue()Left-side HUD: mission title, objectives with checkmarks, progress bar. Does not take NUI focus.
| Export | Parameters | Description |
|---|---|---|
setMission |
data: table |
Show / replace panel |
updateObjective |
objectiveId, completed |
One objective |
updateObjectives |
objectives: table[] |
Many at once |
closeMission |
— | Hide panel |
getMission |
— | Current data or nil |
isMissionActive |
— | boolean |
exports['rm-lib']:setMission({
title = 'Drug Bust Operation',
objectives = {
{ id = 'goto', label = 'Go to the marked location', completed = false },
{ id = 'talk', label = 'Talk to the informant', completed = false },
}
})
exports['rm-lib']:updateObjective('goto', true)
exports['rm-lib']:closeMission()Top-center countdown (e.g. time limit for a delivery or heist). The UI counts down in NUI; optional header and text (e.g. “Timed objective” / instructions). Does not take NUI focus.
| Export | Parameters | Description |
|---|---|---|
showTimer |
data: table, cb?: function |
Start / show timer |
updateTimer |
data: table |
Change seconds and/or header/text without hiding |
closeTimer |
— | Hide timer (does not call cb) |
| Field | Type | Description |
|---|---|---|
seconds |
number |
Required on showTimer. Remaining seconds (integer after rounding). |
header |
string? |
Optional title row (e.g. Timed objective). |
text |
string? |
Optional subtitle / hint. |
If you pass cb to showTimer, it runs once when the countdown hits zero. It does not run if you call closeTimer first.
-- 3 minutes, no header
exports['rm-lib']:showTimer({ seconds = 180 })-- With header + text (typical “timed objective” look)
exports['rm-lib']:showTimer({
seconds = 300,
header = 'Timed objective',
text = 'Reach the drop-off before time runs out.',
}, function()
print('Time expired — fail or trigger next phase')
end)-- Add time or change copy while running
exports['rm-lib']:updateTimer({ seconds = 120, text = '1 minute left!' })exports['rm-lib']:closeTimer()Top-center 0–100 bar (same screen band as the timed objective / timer HUD). Optional header / label. If the timer and progress are both visible, the bar sits below the timer card so they do not overlap.
| Export | Parameters | Description |
|---|---|---|
showProgress |
data: table |
Show bar |
updateProgress |
data: table |
Update value and/or labels |
closeProgress |
— | Hide |
exports['rm-lib']:showProgress({
progress = 25,
header = 'Suspicion',
label = 'Stay out of sight.',
})
exports['rm-lib']:updateProgress({ progress = 80 })
exports['rm-lib']:closeProgress()This is not the same as ox_lib’s lib.progressBar (timed bottom bar). For that, see Duration progress below.
Bottom-center timed progress (label, percent, track). Same Lua behavior and NUI contract as ox_lib (progress / progressCancel, progressComplete callback): animations, props, cancel with X, etc.
| Export | Description |
|---|---|
progressBar |
data — same table as ox_lib lib.progressBar |
progressCircle |
Same NUI bar as progressBar (Lua mirrors ox_lib) |
cancelProgress |
Cancel active bar |
progressActive |
boolean |
local ok = exports['rm-lib']:progressBar({
label = 'Searching...',
duration = 5000,
canCancel = true,
-- anim, prop, disable, ... (same as ox_lib)
})Each resource has its own Lua state. To keep calling lib.progressBar / lib.cancelProgress but show rm-lib NUI instead of ox_lib’s page, add the bridge to that resource’s manifest (after @ox_lib/init.lua). No config.lua — including the file opts in; omit it to keep ox_lib’s default UI.
shared_scripts {
'@ox_lib/init.lua',
'@rm-lib/shared/progress_bridge.lua',
}Requires ensure rm-lib before that resource runs. If rm-lib is not started, the bridge falls back to ox_lib.
Common pattern: show mission objectives and a time limit; when the timer ends, close the timer and react (fail, payout, etc.).
local function startTimedMission()
exports['rm-lib']:setMission({
title = 'Package run',
objectives = {
{ id = 'pickup', label = 'Pick up the cargo', completed = false },
{ id = 'drop', label = 'Deliver to the marker', completed = false },
},
})
exports['rm-lib']:showTimer({
seconds = 420,
header = 'Timed objective',
text = 'Deliver before the clock hits zero.',
}, function()
exports['rm-lib']:closeTimer()
exports['rm-lib']:closeMission()
-- e.g. fail mission, notify player
TriggerEvent('chat:addMessage', { args = { '^1Time up!' } })
end)
endWhile the run is active you can complete objectives as usual:
exports['rm-lib']:updateObjective('pickup', true)If the player finishes in time, clear both:
exports['rm-lib']:closeTimer()
exports['rm-lib']:closeMission()Top-right toasts: dark rounded card, optional title, body text, type (colored icon on a soft tint), same general look as other rm-lib panels.
type |
Use |
|---|---|
info / inform |
Default — info-style icon |
success |
Success styling |
error / fail / failed |
Error styling |
warning is accepted for a fourth style if needed.
| Export | Parameters | Description |
|---|---|---|
notify |
data: table |
Show a toast |
clearNotify |
— | Remove all toasts |
| Field | Type | Description |
|---|---|---|
message or description |
string |
Body text (need title and/or body). |
title |
string? |
Optional line above the message. |
type |
string? |
info / inform, success, error / fail (default info). |
duration |
number? |
Milliseconds (default 3000). |
position, id, icon, iconColor, … |
optional |
Same ideas as ox_lib web notify. |
To keep calling lib.notify from ox_lib but show rm-lib toasts, add the notify bridge in that resource (after @ox_lib/init.lua). No config.lua — including the file opts in; omit it to keep ox_lib’s default notify UI.
shared_scripts {
'@ox_lib/init.lua',
'@rm-lib/shared/notify_bridge.lua',
}Requires ensure rm-lib. If rm-lib is not started, the bridge falls back to ox_lib.
If you want both lib.notify and lib.progressBar routed to rm-lib in the same resource, include both bridges:
shared_scripts {
'@ox_lib/init.lua',
'@rm-lib/shared/notify_bridge.lua',
'@rm-lib/shared/progress_bridge.lua',
}exports['rm-lib']:notify({
message = 'You drank some refreshing water',
type = 'info',
duration = 5000,
})
exports['rm-lib']:notify({
title = 'ATM',
description = 'Withdrawal complete.',
type = 'success',
duration = 4000,
})
exports['rm-lib']:notify({
message = 'Not enough cash.',
type = 'error',
})
exports['rm-lib']:clearNotify()Key + label prompt (e.g. [E] Grab bag) on the right side of the screen. Does not capture NUI focus.
| Export | Parameters | Description |
|---|---|---|
showTextUI |
key: string, label: string |
Show prompt |
hideTextUI |
— | Hide |
exports['rm-lib']:showTextUI('E', 'Grab bag')
exports['rm-lib']:hideTextUI()Uses ox_lib cache.ped when available:
local ped = exports['rm-lib']:getPlayerPed()Useful to align with the same caching strategy as rm-lib internals.