ngd-ui

Custom menu, input, cameras, ped interaction and more!

ngd-ui

Installation Steps

  1. Install ngd-ui
    Put ngd-ui into your resources folder. Preferably into a [ngd] folder that is started after all of your main resources. If you are using this with ngd-Bridge, please refer to the start page to make sure your start order is correct.

  2. Click the UI element below to view it's features.

Helper Text (Click to Expand)

Help Text System

Overview

A bottom-center screen text display system for showing control hints and instructions to players. Supports both vertical and horizontal layouts with GTA color codes.

Features

  • Two Layouts - Vertical (stacked) or horizontal (inline) text display
  • Color Support - Full GTA color code support for highlighting
  • Update Support - Change text without hiding/reshowing
  • Protected Display - Prevents multiple instances from conflicting
  • Zero Configuration - Works out of the box

Usage

Show Help Text

Display help text at the bottom center of the screen.

code
-- Basic usage (vertical layout)
exports['ngd-ui']:ShowHelpText('E: ~g~Interact | G: ~r~Cancel')

-- Horizontal layout
exports['ngd-ui']:ShowHelpText('Arrow Keys: Move | ENTER: ~g~Confirm', 'horizontal')

-- Force show (override existing)
exports['ngd-ui']:ShowHelpText('New text', 'vertical', true)

Update Help Text

Update the text without hiding/reshowing (useful for timers or counters).

code
exports['ngd-ui']:UpdateHelpText('TIME: ~r~5s~w~ remaining')

Hide Help Text

code
exports['ngd-ui']:HideHelpText()

Check if Showing

code
local isShowing = exports['ngd-ui']:IsHelpTextShowing()
if not isShowing then
    -- Safe to show new help text
end

Parameters

ShowHelpText

ParameterTypeRequiredDefaultDescription
textstringYes-Text to display with pipe-separated items
layoutstringNo'vertical''vertical' or 'horizontal'
forcebooleanNofalseOverride existing help text

Returns: boolean - true if shown, false if blocked

UpdateHelpText

ParameterTypeRequiredDescription
textstringYesNew text to display

Returns: boolean - true if updated, false if not showing

Implementation Examples

Placement Mode

code
CreateThread(function()
    while placementMode do
        exports['ngd-ui']:ShowHelpText('Arrow Keys: Move | Q/E: Rotate | ENTER: ~g~Place | BACKSPACE: ~r~Cancel')
        Wait(0)
    end
    exports['ngd-ui']:HideHelpText()
end)

Vehicle Entry

code
CreateThread(function()
    while nearVehicle do
        exports['ngd-ui']:ShowHelpText('F: ~g~Enter Vehicle | H: ~y~Lock/Unlock', 'horizontal')
        Wait(0)
    end
    exports['ngd-ui']:HideHelpText()
end)

Countdown Timer

code
CreateThread(function()
    for i = 10, 0, -1 do
        local color = i <= 3 and '~r~' or '~y~'
        exports['ngd-ui']:UpdateHelpText('TIME: ' .. color .. i .. 's~w~ remaining | SPACE: ~g~Skip')
        Wait(1000)
    end
    exports['ngd-ui']:HideHelpText()
end)

ATM Instructions

code
AddEventHandler('bank:atmOpened', function()
    exports['ngd-ui']:ShowHelpText('Arrow Keys: Navigate | ENTER: ~g~Confirm | BACKSPACE: ~r~Exit')
end)

AddEventHandler('bank:atmClosed', function()
    exports['ngd-ui']:HideHelpText()
end)

Dynamic Interaction Help

code
local function UpdateInteractionHelp(options)
    local helpText = ''
    for i, opt in ipairs(options) do
        if i > 1 then helpText = helpText .. ' | ' end
        helpText = helpText .. opt.key .. ': ' .. opt.color .. opt.label
    end
    exports['ngd-ui']:ShowHelpText(helpText, 'horizontal')
end

-- Usage
UpdateInteractionHelp({
    { key = 'E', color = '~g~', label = 'Open' },
    { key = 'G', color = '~b~', label = 'Lock' },
    { key = 'H', color = '~r~', label = 'Break In' }
})

Color Codes

Use GTA color codes to highlight text:

  • r - Red
  • g - Green
  • b - Blue
  • y - Yellow
  • p - Purple
  • w - White
  • s - Default

Example: 'E: gConfirmw | BACKSPACE: rCancel'

Layout Options

Vertical Layout (Default)

code
E: Open Door
G: Lock Door
H: Break In

Horizontal Layout

code
E: Open Door | G: Lock Door | H: Break In

Best Practices

  1. Always Hide When Done - Call HideHelpText() when the context ends to prevent lingering text
  2. Use in Loops - Call ShowHelpText() every frame in your interaction loops with Wait(0)
  3. Update Don't Replace - Use UpdateHelpText() for changing values (timers, counters) instead of hide/show

Notes

  • Help text automatically prevents multiple instances unless force is true
  • Text remains on screen until manually hidden or overridden
  • Works seamlessly with other UI elements (doesn't block NUI focus)
  • Supports unlimited text length, but keep it concise for readability
Input (Click to Expand)

Input Dialog System

Overview

A comprehensive input dialog system supporting multiple field types including text, password, number, date, time, select, checkbox, and textarea. Perfect for forms, character creation, transactions, and data collection.

Features

  • 9 Input Types - Text, password, number, date, time, select, checkbox, textarea, and multi-select
  • Field Validation - Required fields, min/max values, character limits
  • Rich Formatting - Icons, descriptions, placeholders, default values
  • Flexible Layout - Standard or full-width fields
  • Promise-based - Clean async/await pattern for handling results

Usage

Basic Input Dialog

code
local result = exports['ngd-ui']:InputDialog('Dialog Title', {
    { type = 'input', label = 'Player Name', required = true },
    { type = 'number', label = 'Amount', min = 1, max = 1000 }
}, { allowCancel = true })

if result then
    local name = result[1]
    local amount = result[2]
    -- Process results
end

All Available Input Types

code
local result = exports['ngd-ui']:InputDialog('Form Title', {
    -- Text Input
    {
        type = 'input',
        label = 'Text Field',
        description = 'Enter some text',
        placeholder = 'Type here...',
        icon = 'font',
        required = true,
        min = 3,  -- Min characters
        max = 50  -- Max characters
    },
    
    -- Password Input
    {
        type = 'password',
        label = 'Password',
        placeholder = 'Enter password...',
        icon = 'lock',
        min = 4,
        max = 20
    },
    
    -- Number Input
    {
        type = 'number',
        label = 'Amount',
        icon = 'dollar-sign',
        default = 0,
        min = 0,
        max = 999999,
        precision = 2,  -- Decimal places
        step = 0.01     -- Increment step
    },
    
    -- Date Picker
    {
        type = 'date',
        label = 'Select Date',
        icon = 'calendar',
        format = 'MM/DD/YYYY',
        minDate = '01/01/2020',
        maxDate = '12/31/2025'
    },
    
    -- Time Picker
    {
        type = 'time',
        label = 'Select Time',
        icon = 'clock'
    },
    
    -- Select Dropdown
    {
        type = 'select',
        label = 'Choose Option',
        icon = 'list',
        options = {
            { value = 'option1', label = 'Option 1' },
            { value = 'option2', label = 'Option 2' },
            { value = 'option3', label = 'Option 3' }
        },
        searchable = true  -- Enable search in dropdown
    },
    
    -- Multi-Select
    {
        type = 'multi-select',
        label = 'Choose Multiple',
        icon = 'list-check',
        options = {
            { value = 'a', label = 'Choice A' },
            { value = 'b', label = 'Choice B' },
            { value = 'c', label = 'Choice C' }
        },
        maxSelectedValues = 3
    },
    
    -- Checkbox
    {
        type = 'checkbox',
        label = 'I agree to terms',
        checked = false,
        required = true
    },
    
    -- Textarea
    {
        type = 'textarea',
        label = 'Description',
        placeholder = 'Enter detailed description...',
        autosize = true,
        fullWidth = true  -- Makes dialog wider for this field
    }
})

Parameters

Common Field Properties

PropertyTypeRequiredDefaultDescription
typestringYes'input'Field type (input, password, number, date, time, select, checkbox, textarea)
labelstringYes-Field label displayed to user
descriptionstringNo-Helper text below label
placeholderstringNo-Placeholder text in input
iconstringNo-FontAwesome icon name
defaultanyNo-Default value
requiredbooleanNofalseField is required
disabledbooleanNofalseField is disabled
fullWidthbooleanNofalseMakes dialog wider for this field

Text/Password Specific

PropertyTypeDescription
minnumberMinimum character length
maxnumberMaximum character length

Number Specific

PropertyTypeDescription
minnumberMinimum value
maxnumberMaximum value
precisionnumberDecimal places (e.g., 2 for currency)
stepnumberIncrement/decrement step
hideControlsbooleanHide +/- buttons

Date Specific

PropertyTypeDescription
formatstringDate format (e.g., 'MM/DD/YYYY')
minDatestringMinimum selectable date
maxDatestringMaximum selectable date
clearablebooleanShow clear button
returnStringbooleanReturn as string instead of date object

Select/Multi-Select Specific

PropertyTypeDescription
optionstableArray of {value, label} objects
searchablebooleanEnable search in dropdown
maxSelectedValuesnumberMax selections (multi-select only)

Textarea Specific

PropertyTypeDescription
autosizebooleanAuto-resize based on content

Implementation Examples

Character Creation

code
local result = exports['ngd-ui']:InputDialog('Create Character', {
    { type = 'input', label = 'First Name', icon = 'user', required = true, min = 2, max = 20 },
    { type = 'input', label = 'Last Name', icon = 'user', required = true, min = 2, max = 20 },
    { type = 'date', label = 'Date of Birth', icon = 'calendar', required = true },
    { type = 'number', label = 'Height (cm)', icon = 'ruler-vertical', min = 100, max = 220, default = 170 },
    { type = 'select', label = 'Gender', icon = 'venus-mars', options = {
        { value = 'male', label = 'Male' },
        { value = 'female', label = 'Female' }
    }},
    { type = 'checkbox', label = 'I agree to server rules', required = true }
})

if result then
    local firstName = result[1]
    local lastName = result[2]
    local dob = result[3]
    local height = result[4]
    local gender = result[5]
    local agreedToRules = result[6]
    
    -- Create character
end

Bank Transfer

code
local result = exports['ngd-ui']:InputDialog('Bank Transfer', {
    { type = 'number', label = 'Recipient ID', icon = 'user', required = true, min = 1 },
    { type = 'number', label = 'Amount', icon = 'dollar-sign', required = true, precision = 2, min = 0.01 },
    { type = 'textarea', label = 'Note (optional)', placeholder = 'Add a message...', autosize = true }
})

if result then
    local recipientId = result[1]
    local amount = result[2]
    local note = result[3] or ''
    
    TriggerServerEvent('bank:transfer', recipientId, amount, note)
end

Job Application

code
local result = exports['ngd-ui']:InputDialog('Job Application', {
    { type = 'input', label = 'Full Name', icon = 'user', required = true },
    { type = 'number', label = 'Age', icon = 'calendar', min = 18, max = 99, required = true },
    { type = 'select', label = 'Position', icon = 'briefcase', required = true, options = {
        { value = 'officer', label = 'Police Officer' },
        { value = 'detective', label = 'Detective' },
        { value = 'cadet', label = 'Cadet' }
    }},
    { type = 'textarea', label = 'Why do you want this job?', required = true, fullWidth = true }
})

if result then
    local name = result[1]
    local age = result[2]
    local position = result[3]
    local reason = result[4]
    
    TriggerServerEvent('job:submitApplication', name, age, position, reason)
end

Vehicle Sale

code
local result = exports['ngd-ui']:InputDialog('Sell Vehicle', {
    { type = 'input', label = 'Buyer Name', icon = 'user', required = true },
    { type = 'number', label = 'Sale Price', icon = 'dollar-sign', required = true, precision = 2, min = 1 },
    { type = 'select', label = 'Payment Method', icon = 'credit-card', options = {
        { value = 'cash', label = 'Cash' },
        { value = 'bank', label = 'Bank Transfer' }
    }},
    { type = 'checkbox', label = 'Include vehicle warranty', checked = true }
})

if result then
    TriggerServerEvent('vehicle:processSale', result[1], result[2], result[3], result[4])
end

Store Order Form

code
local result = exports['ngd-ui']:InputDialog('Place Order', {
    { type = 'select', label = 'Product', icon = 'box', required = true, searchable = true, options = {
        { value = 'burger', label = 'Hamburger - $5' },
        { value = 'fries', label = 'French Fries - $3' },
        { value = 'soda', label = 'Soda - $2' }
    }},
    { type = 'number', label = 'Quantity', icon = 'hashtag', min = 1, max = 99, default = 1 },
    { type = 'multi-select', label = 'Extra Toppings', icon = 'plus', options = {
        { value = 'cheese', label = 'Cheese (+$1)' },
        { value = 'bacon', label = 'Bacon (+$2)' },
        { value = 'onions', label = 'Onions (+$0.5)' }
    }},
    { type = 'textarea', label = 'Special Instructions', placeholder = 'Any special requests?' }
})

if result then
    local product = result[1]
    local quantity = result[2]
    local toppings = result[3] or {}
    local instructions = result[4] or ''
    
    TriggerServerEvent('store:placeOrder', product, quantity, toppings, instructions)
end

Return Values

The dialog returns:

  • nil if cancelled
  • table of values in field order if submitted
code
local result = exports['ngd-ui']:InputDialog('Title', {
    { type = 'input', label = 'Name' },
    { type = 'number', label = 'Age' }
})

if result then
    local name = result[1]  -- First field value
    local age = result[2]   -- Second field value
end

Notes

  • Dialog automatically closes when submitted or cancelled
  • Required fields prevent submission if empty
  • Number fields enforce min/max constraints
  • Date fields show a calendar picker
  • Select fields support search with searchable = true
  • Use fullWidth = true on textarea for better multi-line input
  • Icons use FontAwesome class names (without 'fa-' prefix)
ToolMenu Builder
Build menus and full menu→input flows visually, preview them in-game-style, and export ready-to-paste Lua.
Menu (Click to Expand)

Menu System

Overview

A flexible context menu system for creating interactive menus with support for buttons, checkboxes, and nested submenus. Perfect for game menus, interaction menus, and admin panels.

Features

  • Multiple Option Types - Buttons, checkboxes, menu headers
  • Event Triggers - Client events, server events
  • Nested Menus - Support for submenu navigation with history
  • Icon Support - FontAwesome icons or custom images
  • Menu Registration - Register reusable menus by ID
  • Menu-Level Callbacks - onSelected/onSideScroll/onClose report checkbox and scroller selections (ngd-ui ≥ 1.49)

Usage

Simple Menu (CreateMenu)

Create and display a menu immediately.

code
exports['ngd-ui']:CreateMenu({
    { header = 'Main Menu', isMenuHeader = true },
    {
        header = 'Option 1',
        txt = 'Description of option 1',
        icon = 'circle',
        params = {
            event = 'my:clientEvent',
            args = {{ data = 'value', shouldClose = true }}
        }
    },
    {
        header = 'Option 2',
        txt = 'This triggers a server event',
        icon = 'square',
        params = {
            server = true,
            event = 'my:serverEvent',
            args = {{ id = 123, shouldClose = true }}
        }
    }
})

Registered Menu (RegisterMenu + OpenMenu)

Register a menu for reuse and open it later.

code
-- Register the menu
exports['ngd-ui']:RegisterMenu('main_menu', {
    title = 'Main Menu',
    position = 'center-right', -- accepted but currently inert, see Position Options below
    options = {
        { header = 'Settings Menu', isMenuHeader = true },
        {
            header = 'Settings',
            txt = 'Configure your settings',
            icon = 'gear',
            params = {
                event = 'menu:openSettings'
            }
        },
        {
            header = 'Exit',
            txt = 'Close the menu',
            icon = 'times',
            params = {
                event = 'menu:close'
            }
        }
    }
})

-- Open the menu later
exports['ngd-ui']:OpenMenu('main_menu')

Menu Option Properties

PropertyTypeRequiredDescription
headerstringYesMain text displayed
txtstringNoDescription/subtitle text
iconstringNoFontAwesome icon (without 'fa-' prefix) or image path
isMenuHeaderbooleanNoMakes this a non-clickable header
disabledbooleanNoDisable the option
paramstableNoEvent/action parameters
checkedbooleanNoRenders the option as a checkbox with this initial state. Selections report through the menu-level onSelected callback, not through params. Requires ngd-ui ≥ 1.49
valuestableNoRenders the option as a left-right scroller cycling through this array of strings. Selections report through the menu-level onSideScroll callback. Requires ngd-ui ≥ 1.49
defaultIndexnumberNoInitial selected index (0-based) into values. Requires ngd-ui ≥ 1.49

Menu-Level Callbacks

As of ngd-ui ≥ 1.49, the table passed to CreateMenu can carry three optional hash-key callbacks that ngd-ui reads directly off that table. They're plain fields alongside your option list, not extra arguments — the CreateMenu(options, position) signature doesn't change.

CallbackFires whenSignature
onSelectedA row is clicked, or a checked option is toggledfunction(index, listIndex, value)
onSideScrollThe left/right arrows on a values option are usedfunction(index, listIndex, value)
onCloseThe menu closes, for any reason (including after a selecting click on a plain row)function()

index is always the 0-based index into the full options array, counting isMenuHeader rows. onSelected fires with one of three shapes: (index, nil, args) for a plain row's click, (index, nil, checked) when a checked option is toggled, or (index, listIndex, value) when a values option's row (not its arrows) is clicked. onSideScroll fires separately with (index, listIndex, value) when the scroller arrows change the selection.

⚠️ Option-level params do not fire for checked/values options — those selections are only ever reported through onSelected/onSideScroll. Build an index → key lookup table keyed by the full-array index (this is exactly what the Menu Builder generates):

code
local toggleKeys = { [1] = 'hud' }
local scrollerKeys = { [2] = 'volume' }

local function OnSettingsChanged(key, value)
    if key == 'hud' then
        SetHudVisible(value) -- boolean
    elseif key == 'volume' then
        SetAudioVolume(value) -- selected string from `values`
    end
end

local opts = {
    { header = 'Settings', isMenuHeader = true },
    { header = 'HUD', checked = true },
    { header = 'Volume', values = { 'Low', 'Medium', 'High' }, defaultIndex = 1 },
    { header = 'Close' },
}

opts.onSelected = function(index, listIndex, value)
    if listIndex ~= nil then return end -- ignore a scroller row's own click, handled by onSideScroll
    local key = toggleKeys[index]
    if key then OnSettingsChanged(key, value) end
end

opts.onSideScroll = function(index, listIndex, value)
    local key = scrollerKeys[index]
    if key then OnSettingsChanged(key, value) end
end

opts.onClose = function()
    print('Settings menu closed')
end

exports['ngd-ui']:CreateMenu(opts)

Params Properties

PropertyTypeDescription
eventstringEvent name to trigger
argstableArguments to pass to event
serverbooleanIf true, triggers server event

⚠️ Important Note on Args: args must be wrapped in the double-brace form — args = { { key = value } } — the runtime unpacks args[1] when it triggers your event, so the inner table is what your handler receives. The menu itself always closes on selection regardless of shouldClose — that flag controls only whether NUI focus (the mouse cursor) is released. It applies to client events only: include shouldClose = true inside the inner table when a client event ends the flow, or the cursor stays captured after the menu closes. Server events (server = true) always release focus, so shouldClose has no effect there.

Menu Functions

CreateMenu

Create and immediately display a menu.

code
exports['ngd-ui']:CreateMenu(options, position)

Parameters:

  • options - Table of menu options. May also carry the onSelected/onSideScroll/onClose hash-key callbacks — see Menu-Level Callbacks above
  • position - Currently has no effect; the menu panel always renders fixed at the right side of the screen regardless of the value passed

RegisterMenu

Register a menu for later use.

code
exports['ngd-ui']:RegisterMenu(id, menuData)

Parameters:

  • id - Unique menu identifier
  • menuData - Table containing title, position, options

OpenMenu

Open a previously registered menu.

code
exports['ngd-ui']:OpenMenu(id, keepHistory)

Parameters:

  • id - Menu identifier
  • keepHistory - If true, adds to navigation history - Default: false

CloseMenu

Close the currently open menu.

code
exports['ngd-ui']:CloseMenu()

GoBackMenu

Navigate to the previous menu in history.

code
exports['ngd-ui']:GoBackMenu()

Only applies to menus opened through RegisterMenu/OpenMenu — menu history is populated by OpenMenu, so a CreateMenu flow has nothing to go back to. For a "Back" option inside a CreateMenu flow, give the option an event that re-opens the parent menu directly instead of calling GoBackMenu().

UpdateMenuOptions

Update options of a registered menu.

code
exports['ngd-ui']:UpdateMenuOptions(id, options)

IsMenuOpen

Check if any menu is currently open.

code
local isOpen = exports['ngd-ui']:IsMenuOpen()

GetOpenMenu

Get the ID of the currently open menu.

code
local menuId = exports['ngd-ui']:GetOpenMenu()

Implementation Examples

Crafting Menu

code
exports['ngd-ui']:CreateMenu({
    { header = 'Pour Wine Menu', isMenuHeader = true },
    {
        header = 'Red Wine',
        txt = 'Required: Grapes, Sugar',
        icon = 'wine-bottle',
        params = {
            event = 'wine:pour',
            args = {{ drink = 'red_wine' }}
        }
    },
    {
        header = 'White Wine',
        txt = 'Required: White Grapes, Sugar',
        icon = 'wine-glass',
        params = {
            server = true,
            event = 'wine:pourServer',
            args = {{ drink = 'white_wine' }}
        }
    },
    {
        header = 'Champagne',
        txt = 'Required: Special Grapes',
        icon = 'glass-cheers',
        disabled = true
    }
})

Vehicle Interaction Menu

code
exports['ngd-ui']:CreateMenu({
    { header = 'Vehicle Options', isMenuHeader = true },
    {
        header = 'Toggle Engine',
        txt = 'Turn engine on/off',
        icon = 'car',
        params = {
            event = 'vehicle:toggleEngine'
        }
    },
    {
        header = 'Open Trunk',
        txt = 'Access vehicle storage',
        icon = 'box',
        params = {
            event = 'vehicle:openTrunk'
        }
    },
    {
        header = 'Lock/Unlock',
        txt = 'Toggle vehicle locks',
        icon = 'lock',
        params = {
            event = 'vehicle:toggleLock'
        }
    }
})

Position Options

The position value — passed to CreateMenu, or set as menuData.position for a registered menu — is currently inert. The menu panel always renders fixed at the right side of the screen; none of the values below have any visual effect yet:

  • 'center-right'
  • 'center-left'
  • 'top-left'
  • 'top-right'
  • 'bottom-left'
  • 'bottom-right'
  • 'center'

Event Handling

Client Event

code
RegisterNetEvent('myEvent', function(args)
    print('Received:', args.data)
end)

Server Event

code
RegisterNetEvent('myServerEvent', function(args)
    local src = source
    print('Player:', src, 'Args:', args.id)
end)

Checkbox / Scroller Selections

Checkbox (checked) and scroller (values) options don't emit RegisterNetEvent-style events at all — they report only through the menu-level onSelected/onSideScroll callbacks. See Menu-Level Callbacks above.

Icon Usage

Icons can be specified in two ways:

FontAwesome Icons

Use the icon name without the fa- prefix:

code
icon = 'circle'        -- renders as fa-solid fa-circle
icon = 'gear'          -- renders as fa-solid fa-gear
icon = 'wine-bottle'   -- renders as fa-solid fa-wine-bottle

Custom Images

Use NUI path for custom images:

code
icon = 'nui://myresource/images/custom.png'

Notes

  • Menus automatically handle NUI focus (mouse cursor)
  • args uses the double-brace form: args = { { key = value } } — the runtime unpacks args[1] when it triggers the event
  • Menu headers with isMenuHeader = true are non-clickable section labels
  • Checkbox (checked) and scroller (values) selections report only through the menu-level onSelected/onSideScroll callbacks (ngd-ui ≥ 1.49) — option-level params never fire for them
  • GoBackMenu() and menu history only apply to RegisterMenu/OpenMenu menus; a CreateMenu flow has no history to go back to — give a "Back" option an event that re-opens the parent menu instead
  • position is currently inert — the menu panel always renders fixed at the right side of the screen
  • Icons without path prefix are treated as FontAwesome icons
Security Camera (Click to Expand)

Security Camera System

Overview

The Security Camera System provides an optimized camera viewing interface with zero idle CPU usage when cameras are not active. Players can cycle through multiple security cameras with keyboard controls and a clean HUD overlay.

Features

  • Zero Idle Performance Impact - No CPU usage when cameras are inactive
  • Multiple Camera Support - Configure unlimited cameras per system
  • Smooth Navigation - Arrow key controls to switch between cameras
  • System Branding - Custom system name and subtitle for each installation
  • HUD Integration - Automatic HUD hiding with camera info overlay
  • Debounced Switching - 500ms cooldown prevents accidental double-switches

Configuration

Basic Setup

code
Config.CameraSystem = {
    systemName = "Daddy Tobacco",
    systemSubtitle = "Security System",
    cameras = {
        { 
            coords = vector3(-34.05, 2895.45, 63.90), 
            heading = 225.31, 
            pitch = -20.0, 
            roll = 0.0, 
            name = "Front Gate" 
        },
        { 
            coords = vector3(-72.86, 2927.90, 65.13), 
            heading = 277.27, 
            pitch = -35.0, 
            roll = 0.0, 
            name = "North Sideyard" 
        },
    }
}

Camera Properties

PropertyTypeRequiredDescription
coordsvector3YesCamera position in world space
headingfloatYesCamera yaw rotation (0-360)
pitchfloatNoCamera pitch rotation (default: 0.0)
rollfloatNoCamera roll rotation (default: 0.0)
namestringYesDisplay name shown in UI

System Properties

PropertyTypeRequiredDescription
systemNamestringNoMain title (e.g., "Daddy Tobacco")
systemSubtitlestringNoSubtitle (e.g., "Security System")
camerastableYesArray of camera configurations

Usage

Opening Camera System

code
-- With full configuration
exports['ngd-ui']:OpenCameras(Config.CameraSystem)

-- With just cameras array (no system name/subtitle)
exports['ngd-ui']:OpenCameras({
    { coords = vector3(x, y, z), heading = 0.0, name = "Camera 1" },
    { coords = vector3(x, y, z), heading = 90.0, name = "Camera 2" }
})

Closing Camera System

code
exports['ngd-ui']:CloseCameras()

Manual Camera Navigation

code
-- Switch to next camera
exports['ngd-ui']:NextCamera()

-- Switch to previous camera
exports['ngd-ui']:PreviousCamera()

Controls

KeyAction
Left Arrow (174)Previous camera
Right Arrow (175)Next camera
Backspace (194)Exit camera system

Exports

OpenCameras

Opens the camera viewing system.

Parameters:

  • data (table): Configuration table or camera array

Example:

code
exports['ngd-ui']:OpenCameras({
    systemName = "Prison",
    systemSubtitle = "Maximum Security",
    cameras = {
        { coords = vector3(1845.0, 2585.0, 45.0), heading = 180.0, name = "Cell Block A" }
    }
})

CloseCameras

Closes the camera system and restores normal view.

Example:

code
exports['ngd-ui']:CloseCameras()

NextCamera

Switches to the next camera in the sequence (wraps to first camera at end).

Example:

code
exports['ngd-ui']:NextCamera()

PreviousCamera

Switches to the previous camera in the sequence (wraps to last camera at start).

Example:

code
exports['ngd-ui']:PreviousCamera()

Implementation Examples

Example 1: Simple Security Office

code
RegisterCommand('officesecurity', function()
    exports['ngd-ui']:OpenCameras({
        systemName = "Downtown Office",
        systemSubtitle = "Security Cameras",
        cameras = {
            { coords = vector3(-75.0, -818.0, 326.0), heading = 90.0, pitch = -25.0, name = "Lobby" },
            { coords = vector3(-75.0, -827.0, 326.0), heading = 180.0, pitch = -30.0, name = "Elevator" },
            { coords = vector3(-68.0, -823.0, 326.0), heading = 270.0, pitch = -20.0, name = "Parking" }
        }
    })
end)

Example 2: Store Security System

code
local storeCameras = {
    systemName = "24/7 Supermarket",
    systemSubtitle = "Store #143",
    cameras = {
        { coords = vector3(28.0, -1339.0, 31.0), heading = 225.0, pitch = -30.0, name = "Entrance" },
        { coords = vector3(25.0, -1345.0, 31.0), heading = 135.0, pitch = -25.0, name = "Counter" },
        { coords = vector3(31.0, -1343.0, 31.0), heading = 315.0, pitch = -20.0, name = "Aisles" }
    }
}

-- Triggered by interaction with security monitor
RegisterNetEvent('store:viewCameras', function()
    exports['ngd-ui']:OpenCameras(storeCameras)
end)

Notes

  • No idle CPU usage when cameras are inactive
  • 500ms cooldown between camera switches prevents accidental double-switching
  • Camera rotation uses standard FiveM format: Heading (0-360°), Pitch (negative looks down), Roll (usually 0.0)
Flipbook (Click to Expand)

Flipbook/Menu System

Overview

A visual flipbook system for displaying multi-page menus, catalogs, or documents. Players can flip through pages with a clean interface and optional title/subtitle branding.

Features

  • Multi-Page Support - Display unlimited pages with automatic numbering
  • Custom Branding - Optional title and subtitle for each flipbook
  • Flexible Format - Supports JPG, PNG, and other image formats
  • Resource Integration - Load images from any resource using NUI paths
  • Simple Controls - Arrow keys or buttons to flip pages

Configuration

Basic Setup

code
Config.Menu = {
    title = "Vanilla Unicorn",
    subtitle = "Drink Menu",
    imagePath = "nui://ngd-vu/web/images/",
    pages = 2,
    imageFormat = "jpg"
}

Image File Structure

Your images should be named sequentially starting from 1:

code
your-resource/
  web/
    images/
      1.jpg
      2.jpg
      3.jpg

Usage

Opening a Flipbook

code
-- With full configuration
exports['ngd-ui']:OpenFlipbook(Config.Menu)

-- Inline configuration
exports['ngd-ui']:OpenFlipbook({
    title = "Gun Store",
    subtitle = "Available Weapons",
    imagePath = "nui://myresource/web/pages/",
    pages = 5
})

-- Simple version (no title/subtitle)
exports['ngd-ui']:OpenFlipbook({
    imagePath = "nui://ngd-vu/web/images/",
    pages = 2
})

Closing a Flipbook

code
exports['ngd-ui']:CloseFlipbook()

Parameters

ParameterTypeRequiredDefaultDescription
titlestringNo''Main title displayed on flipbook
subtitlestringNo''Subtitle displayed under title
imagePathstringYes-NUI path to image folder (must end with /)
pagesnumberYes-Number of page images
imageFormatstringNo'jpg'Image file extension (jpg, png, etc.)

Implementation Examples

Restaurant Menu

code
exports['ngd-ui']:OpenFlipbook({
    title = "Burger Shot",
    subtitle = "Menu",
    imagePath = "nui://burgershot/web/menu/",
    pages = 4,
    imageFormat = "png"
})

Nightclub Drink Menu

code
local vuMenu = {
    title = "Vanilla Unicorn",
    subtitle = "Drink Menu",
    imagePath = "nui://ngd-vu/web/images/",
    pages = 2
}

RegisterNetEvent('vu:openMenu', function()
    exports['ngd-ui']:OpenFlipbook(vuMenu)
end)

Clothing Catalog

code
exports['ngd-ui']:OpenFlipbook({
    title = "Ponsonbys",
    subtitle = "Spring Collection 2024",
    imagePath = "nui://clothing-store/catalog/",
    pages = 8
})

Gun Store Catalog

code
AddEventHandler('gunstore:openCatalog', function()
    exports['ngd-ui']:OpenFlipbook({
        title = "Ammu-Nation",
        subtitle = "Firearms Catalog",
        imagePath = "nui://gunstore/web/catalog/",
        pages = 6,
        imageFormat = "png"
    })
end)

Simple Product Catalog (No Branding)

code
exports['ngd-ui']:OpenFlipbook({
    imagePath = "nui://my-store/products/",
    pages = 3
})

Image Preparation Tips

  1. Naming Convention: Name your images sequentially: 1.jpg, 2.jpg, 3.jpg, etc.
  2. Resolution: Recommended 1920x1080 or 16:9 aspect ratio for best display
  3. File Size: Optimize images to reduce load times (under 500KB per image recommended)
  4. Path: Always end imagePath with a forward slash /

NUI Path Examples

code
-- From your resource
imagePath = "nui://my-resource/web/images/"

-- From another resource
imagePath = "nui://qb-shops/html/menus/"

-- Nested folders
imagePath = "nui://server-ui/public/catalogs/guns/"

Notes

  • Players can close the flipbook by pressing ESC or clicking the close button
  • Images are loaded dynamically as players flip through pages
  • The imagePath must be a valid NUI path accessible from your resource
  • Image files must be sequential starting from 1 (no gaps in numbering)
3D Drawtext (Click to Expand)

3D Draw Text & Interaction System

Overview

An optimized 3D text rendering and interaction system that replaces traditional target systems. Features distance-based rendering, persistent markers, and interactive prompts with zero idle CPU usage.

Features

  • Interaction System - Target replacement with keypress detection
  • Text Markers - Persistent 3D text labels (no interaction)
  • 3D Text - Persistent text that auto-manages rendering
  • Draw3DText - Simple draw function for manual loops
  • Smart Sleep - Distance-based thread optimization

Configuration

None required. Optional debug flag: Config.ThreeDTextDebug = true

Usage

Interaction System

Create interactive points that respond to key presses.

Single Option

code
exports['ngd-ui']:AddInteraction({
    id = 'shop_door',
    coords = vector3(25.0, -1347.0, 29.5),
    key = 'E',
    keyLabel = 'E',
    label = 'Open Shop',
    icon = 'fa-solid fa-store',
    color = '~g~',
    range = 2.5,
    onInteract = function(id, data)
        -- Your code here
    end
})

Multiple Options

code
exports['ngd-ui']:AddInteraction({
    id = 'boss_safe',
    coords = vector3(25.0, -1347.0, 29.5),
    icon = 'fa-solid fa-briefcase',
    range = 3.0,
    options = {
        {
            key = 'E',
            keyLabel = 'E',
            label = 'Open Stash',
            color = '~g~',
            onInteract = function()
                -- Your code here
            end
        },
        {
            key = 'G',
            keyLabel = 'G',
            label = 'Boss Menu',
            color = '~b~',
            job = 'police',
            onInteract = function()
                -- Your code here
            end
        }
    }
})

Managing Interactions

code
-- Update label
exports['ngd-ui']:UpdateInteraction('shop_door', 'Shop Closed')

-- Update position
exports['ngd-ui']:UpdateInteractionCoords('shop_door', vector3(x, y, z))

-- Remove specific interaction
exports['ngd-ui']:RemoveInteraction('shop_door')

-- Clear all interactions
exports['ngd-ui']:ClearAllInteractions()

Text Markers

Display 3D text without interaction (informational only).

code
-- Add marker
exports['ngd-ui']:AddTextMarker(
    'parking_sign',
    vector3(215.0, -810.0, 30.0),
    'Public Parking',
    10.0,
    'fa-solid fa-parking'
)

-- Update text
exports['ngd-ui']:UpdateTextMarker('parking_sign', 'Parking Full')

-- Update position
exports['ngd-ui']:UpdateTextMarkerCoords('parking_sign', vector3(x, y, z))

-- Remove marker
exports['ngd-ui']:RemoveTextMarker('parking_sign')

-- Clear all markers
exports['ngd-ui']:ClearAllTextMarkers()

Persistent 3D Text

Auto-managed 3D text that handles its own rendering loop.

code
-- Add persistent text
exports['ngd-ui']:Add3DText(
    'welcome_sign',
    vector3(0.0, 0.0, 70.0),
    'Welcome to Los Santos',
    15.0,
    'fa-solid fa-star'
)

-- Update text
exports['ngd-ui']:Update3DText('welcome_sign', 'Welcome Back!')

-- Update position
exports['ngd-ui']:Update3DTextCoords('welcome_sign', vector3(x, y, z))

-- Remove text
exports['ngd-ui']:Remove3DText('welcome_sign')

-- Clear all texts
exports['ngd-ui']:ClearAll3DTexts()

Draw3DText (Manual Loop)

Simple draw function for use inside your own loops.

code
CreateThread(function()
    while someCondition do
        exports['ngd-ui']:Draw3DText(
            vector3(x, y, z),
            'E: ~g~Interact',
            10.0,
            'fa-solid fa-hand'
        )
        Wait(0)
    end
    exports['ngd-ui']:Hide3DText()
end)

Parameters

Interaction Options

ParameterTypeRequiredDefaultDescription
idstringYes-Unique identifier
coordsvector3Yes-World position
keystring/numberNo-Key to press (single option)
keyLabelstringNo'E'Display label for key
labelstringNo-Text to display (single option)
iconstringNo-FontAwesome icon class
colorstringNo'g'GTA color code
rangenumberNo2.5Interaction range
jobstring/tableNo-Required job(s)
notJobstring/tableNo-Excluded job(s)
onInteractfunctionNo-Callback function
optionstableNo-Array of multiple options

Text Marker Parameters

ParameterTypeRequiredDefaultDescription
idstringYes-Unique identifier
coordsvector3Yes-World position
textstringYes-Text to display
rangenumberNo5.0Visibility range
iconstringNo-FontAwesome icon class

3D Text Parameters

ParameterTypeRequiredDefaultDescription
idstringYes-Unique identifier
coordsvector3Yes-World position
textstringYes-Text to display
distancenumberNo10.0Visibility distance
iconstringNo-FontAwesome icon class

Color Codes

Use GTA color codes in your text:

  • r - Red
  • g - Green
  • b - Blue
  • y - Yellow
  • p - Purple
  • w - White
  • s - Default

Example: 'E: gOpen w| rG: Close'

Job Filtering

code
-- Single job
job = 'police'

-- Multiple jobs
job = {'police', 'ambulance'}

-- Exclude jobs
notJob = 'unemployed'
notJob = {'unemployed', 'civilian'}

Implementation Examples

Store Entrance

code
exports['ngd-ui']:AddInteraction({
    id = 'store_24_7',
    coords = vector3(25.0, -1347.0, 29.5),
    key = 'E',
    label = 'Open Store',
    icon = 'fa-solid fa-store',
    range = 2.0,
    onInteract = function()
        TriggerEvent('store:openMenu')
    end
})

ATM Machine

code
exports['ngd-ui']:AddInteraction({
    id = 'atm_main_1',
    coords = vector3(-386.7, 6046.0, 31.5),
    key = 'E',
    label = 'Use ATM',
    icon = 'fa-solid fa-credit-card',
    color = '~b~',
    range = 1.5,
    onInteract = function()
        TriggerEvent('bank:openATM')
    end
})

Police Evidence Locker

code
exports['ngd-ui']:AddInteraction({
    id = 'pd_evidence',
    coords = vector3(475.0, -995.0, 26.3),
    icon = 'fa-solid fa-box-archive',
    range = 2.0,
    options = {
        {
            key = 'E',
            label = 'View Evidence',
            job = 'police',
            onInteract = function()
                -- Open evidence UI
            end
        },
        {
            key = 'G',
            label = 'Submit Evidence',
            job = 'police',
            color = '~y~',
            onInteract = function()
                -- Submit evidence
            end
        }
    }
})

Parking Zone Sign

code
exports['ngd-ui']:AddTextMarker(
    'parking_downtown',
    vector3(215.0, -810.0, 30.0),
    '~b~Public Parking~w~\n$5 per hour',
    12.0,
    'fa-solid fa-square-parking'
)

Notes

  • Interactions use Wait(0) when player is in range for precise key detection
  • Out of range, threads use smart sleep (100ms-1000ms) based on distance
  • Text scale automatically adjusts based on distance (0.7-1.0)
  • All loops automatically stop when no elements are registered
Notification System (Click to Expand)

Notification System

Overview

A clean, modern notification system for FiveM that supports icons, sounds, positions, themes, and server-side triggering. Offers two interfaces — standard Notify and advanced Notify with icon support.

Features

  • Simple syntax — one-line usage
  • Themed notifications — success, error, warning, info, default
  • Position control — choose where your notifications display
  • Progress bar toggle — show or hide the bar
  • Icon support — FontAwesome icons
  • Sound toggle

Parameters

Notify

ParameterTypeRequiredDefaultDescription
titlestringYes-Main text heading
messagestringNo''Secondary text line
timenumberYes5000Duration in ms
typestringNo'default'success | error | warning | info | default
positionstringNo'center-right'See position list below
hideprogressbarbooleanNofalseHides progress bar
soundbooleanNotruePlays notification sound

Usage

Notify (Standard)

code
exports['ngd-ui']:Notify(
    title,              -- string (required)
    message,            -- string (optional)
    time,               -- number in ms (required)
    type,               -- string (optional)
    position,           -- string (optional)
    hideprogressbar,    -- boolean (optional)
    sound               -- boolean (optional)
)

NotifyAdvanced (With Icon Support)

code
exports['ngd-ui']:NotifyAdvanced({
    title       = "",
    message     = "",
    time        = 5000,
    type        = "default",
    icon        = "",       -- FontAwesome icon name
    position    = "",
    hideProgressBar = false,
    sound       = true
})

Examples

Basic Notification

code
exports['ngd-ui']:Notify("Success!", "You did the thing", 5000, "success")

Error Notification

code
exports['ngd-ui']:Notify("Error", "Something went wrong", 5000, "error")

Custom Position

code
exports['ngd-ui']:Notify("Warning", "Low fuel detected", 5000, "warning", "top-center")

Without Progress Bar

code
exports['ngd-ui']:Notify("Note", "Progress bar disabled", 5000, "info", "top-right", true)

No Sound

code
exports['ngd-ui']:Notify("Shhh...", "Sound disabled for this notification", 5000, "info", nil, nil, false)

Advanced Notification Examples

Custom Icon Notification

code
exports['ngd-ui']:NotifyAdvanced({
    title    = "Vehicle Stored",
    message  = "Your vehicle has been stored in the garage",
    time     = 5000,
    type     = "success",
    icon     = "car",
    position = "top-right"
})

Server-Side Usage

Trigger a Client Notification from Server

code
TriggerClientEvent('ngd-ui:Notify', source, "Job Complete!", "You earned $500", 5000, "success")

NotifyAdvanced

FieldTypeRequiredDefaultDescription
titlestringYes-Main text heading
messagestringNo''Secondary text line
timenumberYes5000Duration in ms
typestringNo'default'success | error | warning | info | default
iconstringNonilFontAwesome icon name
positionstringNo'center-right'See position list
hideProgressBarbooleanNofalseHides progress bar
soundbooleanNotruePlays sound

Returns: none


Position Options

code
top-right
top-left
top-center
bottom-right
bottom-left
bottom-center
center-right (default)
above-center-right

Notification Types

TypeColor/Style
successGreen highlight
errorRed highlight
warningYellow highlight
infoBlue highlight
defaultNeutral style

Example Implementation

Item Pickup

code
AddEventHandler('items:pickup', function(label)
    exports['ngd-ui']:Notify("Picked Up", "You collected: " .. label, 3000, "success")
end)

Fuel Warning

code
if fuel <= 5 then
    exports['ngd-ui']:Notify("Fuel Low", "Find a gas station!", 5000, "warning", "bottom-center")
end

Crafting Complete

code
exports['ngd-ui']:NotifyAdvanced({
    title = "Crafting Complete",
    message = "Your item is ready to use!",
    type = "success",
    icon = "hammer",
    sound = true
})

Simple Shops (Click to Expand)

Shop UI System

Overview

A flexible NUI-based shop interface for FiveM that supports dynamic shop data, custom server events, and automatic item image paths. Intended for clean, modern shop implementations using a single data format.


Features

  • Config-based shops – reusable, structured shop definitions
  • Inline shops – create shops directly from code
  • Custom server events – handle purchases in any resource
  • Image auto-resolution – automatic full nui:// path building
  • Slot sorting – automatic sort by slot ordering
  • Simple exports – OpenShop and CloseShop

Exports

OpenShop

code
exports['ngd-ui']:OpenShop(data)

data should be a shop table containing:

code
{
    name        = "Shop Name",
    imagePath   = "path/to/images/",
    serverEvent = "myresource:Server:Purchase",
    items = {
        { name = "water", label = "Water", price = 5 },
        ...
    }
}

CloseShop

code
exports['ngd-ui']:CloseShop()

Closes the currently open shop and releases UI focus.


Shop Data Format

Structure

code
local shop = {
    name        = "My Shop",
    imagePath   = "nui://ox_inventory/web/images/",
    serverEvent = "myresource:Server:Purchase",
    items = {
        { name = "vodka", label = "Vodka", price = 10 },
        ...
    }
}

Open with:

code
exports['ngd-ui']:OpenShop(shop)

Item Fields

FieldTypeRequiredDefaultDescription
namestringYes-Item database name
labelstringNoautoDisplay label in UI
pricenumberNo0Price per item
countnumberNonilOptional stock control
imagestringNoautoCustom image path
slotnumberNoautoDisplay ordering
infotableNonilExtra purchase info
typestringNonilFor your own logic

Image Handling

The UI will automatically:

  • Normalize image paths
  • Append missing prefixes (nui://)
  • Append missing file extensions if needed
  • Build fallback images using item names

Example fallback:

code
imagePath .. "water.png"

Server Event Behavior

When a shop purchase occurs, the client sends:

code
local purchaseData = {
    shopId = data.shopId,
    items  = data.items,
    total  = data.total
}

If a custom serverEvent is defined

code
TriggerServerEvent("myresource:Server:Purchase", purchaseData)

If no serverEvent is defined

The shop system defaults to:

code
TriggerServerEvent("ngd-ui:Server:ShopPurchase", purchaseData)

Inline Example

code
exports['ngd-ui']:OpenShop({
    name        = "Snack Bar",
    serverEvent = "my_snacks:Server:Purchase",
    items = {
        { name = "water",   label = "Water Bottle", price = 5 },
        { name = "sandwich", label = "Sandwich",    price = 25 }
    }
})

Config Example

code
Config = Config or {}

Config.AlcoholStore = {
    name        = "Alcohol Store",
    imagePath   = "nui://ngd-vu/web/images/",
    serverEvent = "ngd-vu:Server:StorePurchases",
    items = {
        { name = "vodka", label = "Vodka", price = 10 },
        { name = "whiskey", label = "Whiskey", price = 15 }
    }
}

exports['ngd-ui']:OpenShop(Config.AlcoholStore)

Closing the Shop

code
exports['ngd-ui']:CloseShop()

This gets called automatically when the shop items are purchased or the shop gets closed out. This is just useful for your other resources to potentially close the shop menu. (Maybe if a player dies while in a shop?)


Progress Bars (Click to Expand)

Progress Bar System

Overview

A flexible progress bar system for ngd-ui that is compatible with common progressbar-style exports.
Supports linear and circular progress bars, animations, props, control disabling, cancellation, and both synchronous and callback-based usage.


Features

  • Drop-in compatible with progressbar-style data and callbacks
  • Two styles – standard bar and circular progress
  • Cancelable – X or BACKSPACE (configurable via canCancel)
  • Control locking – movement, vehicle, mouse, and combat
  • Animations & props – play an animation and attach one or two props
  • Synchronous or async – return boolean or use a callback
  • Dead checks – optional useWhileDead protection
  • Helpers – cancel & state check exports

Exports

code
exports['ngd-ui']:Progress(data, cb?)         -- linear bar
exports['ngd-ui']:ProgressCircle(data, cb?)   -- circular bar
exports['ngd-ui']:CancelProgress()            -- force-cancel active progress
exports['ngd-ui']:IsDoingProgress()           -- returns boolean

-- Optional direct sync access (internal helpers)
exports['ngd-ui']:ProgressSync(data)
exports['ngd-ui']:ProgressCircleSync(data)

cb? = optional callback


Basic Usage

1. Simple linear bar (callback style)

code
exports['ngd-ui']:Progress({
    label      = "Lockpicking...",
    duration   = 5000, -- ms
    canCancel  = true,
    controlDisables = {
        disableMovement    = true,
        disableCarMovement = true,
        disableMouse       = false,
        disableCombat      = true
    }
}, function(cancelled)
    if not cancelled then
        print("Success!")
    else
        print("Cancelled")
    end
end)

2. Simple linear bar (sync / blocking)

code
local success = exports['ngd-ui']:Progress({
    label    = "Searching...",
    duration = 3000
})

if success then
    print("Search complete")
else
    print("Search cancelled or failed")
end

3. Circular progress bar

code
exports['ngd-ui']:ProgressCircle({
    label     = "Scanning Area...",
    duration  = 7000,
    canCancel = true
}, function(cancelled)
    if not cancelled then
        print("Scan finished")
    end
end)

Data Format

The system accepts one unified data table and will internally normalise common progressbar fields.

Top-level fields

FieldTypeRequiredDefaultDescription
labelstringNo"Loading..."Text shown in the progress UI
namestringNoused if label is nilAlternative label (progressbar-style)
durationnumberYes-Duration in milliseconds
canCancelbooleanNofalseWhether player can cancel with X/BACKSPACE
useWhileDeadbooleanNonilIf false and ped is dead, progress is cancelled immediately
disabletableNoderivedExplicit control disable table (see below)
controlDisablestableNo-Common progressbar-style control flags (mapped to disable)
animtableNoderivedAnimation definition (see below)
animationtableNo-Progressbar-style animation table (mapped to anim)
proptableNonilProp to attach (see props)
propTwotableNonilSecond prop to attach

Control Disables

You may use either disable (native format) or controlDisables (progressbar-style).

disable format

code
disable = {
    disableMovement = true,
    disableMouse    = false,
    disableCar      = true,
    disableCombat   = true
}

controlDisables (mapped automatically)

code
controlDisables = {
    disableMovement    = true,
    disableCarMovement = true,
    disableMouse       = false,
    disableCombat      = true
}

Mapped internally to:

  • disable.disableCar = disableCarMovement or disableCar

Animations

You can play an animation during the progress bar.

Native anim format

code
anim = {
    dict = "amb@world_human_hammering@male@base",
    anim = "base",
    flag = 49 -- optional
}

Progressbar-style animation (mapped automatically)

code
animation = {
    animDict = "amb@world_human_bum_wash@male@low@idle_a",
    anim     = "idle_a",
    flag     = 49,   -- or
    flags    = 49
}

If both are supplied, anim takes priority.

Animations are started before the bar and stopped automatically when it completes or is cancelled.


Props

You can optionally attach one or two props to the ped during progress.

Prop fields

Each prop / propTwo may contain:

FieldTypeRequiredDescription
modelstring/intYesModel name or hash
bonenumberNoPed bone index (default: 60309 hand)
posvector3NoOffset position relative to bone
rotvector3NoRotation relative to bone
coordstableNo{ x, y, z } alternative to pos
rotationtableNo{ x, y, z } alternative to rot

Example:

code
prop = {
    model = "prop_tool_hammer",
    bone  = 57005,
    pos   = vector3(0.1, 0.02, -0.02),
    rot   = vector3(90.0, 0.0, 0.0)
}

coords / rotation are automatically converted into vector3 if provided instead of pos / rot.

Props are deleted automatically after completion or cancellation.


Cancellation

If canCancel = true, the player can cancel using:

  • X key (INPUT_VEH_DUCK, control 73)
  • BACKSPACE key (control 177)

Internally, the system:

  • Sends cancelProgress / cancelProgressCircle to NUI
  • Resolves the promise with { completed = false, cancelled = true }
  • Sets isDoingProgress back to false

For callback-based usage, the callback receives:

code
function(cancelled)
    if cancelled then
        -- user cancelled or failed
    else
        -- success
    end
end

For synchronous usage, Progress / ProgressCircle return:

  • true → completed
  • false → cancelled or failed

CancelProgress Export

code
exports['ngd-ui']:CancelProgress()

Force-cancel whichever progress (bar or circle) is active.
Safe to call even if no progress is running.

Typical use:

code
if exports['ngd-ui']:IsDoingProgress() then
    exports['ngd-ui']:CancelProgress()
end

IsDoingProgress Export

code
local active = exports['ngd-ui']:IsDoingProgress()
if active then
    print("Progress in progress...")
end

Returns true if a progress bar or circle is currently running, otherwise false.


Notes & Best Practices

  1. Always set duration – progress without a duration is rejected and logged as an error.
  2. Use callbacks for non-blocking flows – when running in threads that shouldn’t block gameplay logic.
  3. Use useWhileDead = false when the action shouldn’t be possible while dead.
  4. Avoid double starts – if Progress is called while another is active, it returns false and does nothing.
  5. Keep labels short – long labels may wrap or clip depending on UI design.

ToolDialog Builder
Build ped dialogs visually — drag out your conversation as a flow chart and export ready-to-paste code. No hand-written dialog tables needed.
PED Dialog (Click to Expand)

Dialog / PED Interaction System

Overview

A lightweight dialog system for ngd-ui that lets you open an interactive conversation UI with any PED or object.
It supports titles, icons, descriptions, tags, and a list of clickable choices that can trigger client events, server events, commands, or Lua functions.
When opened, a cinematic camera focuses on the target PED for a more immersive interaction.


Features

  • Cinematic camera focused on the interacting PED
  • Custom dialog content – title, icon, description, tags
  • Multi-choice interactions per dialog
  • Supported action types:
    • client events
    • server events
    • commands
    • custom Lua functions
  • Dynamic dialog updates without closing the UI
  • Automatic focus management between game + UI

Exports

TalkToNPC

code
exports['ngd-ui']:TalkToNPC(entity, dialog)

Opens the dialog UI for a given PED or entity.

Parameters

NameTypeRequiredDescription
entitynumberYesEntity handle (usually a PED/object)
dialogtableYesDialog configuration (structure below)

UpdateDialog

code
exports['ngd-ui']:UpdateDialog(dialog)

Updates the currently open dialog contents.
Useful for multi-step interactions, branching dialogs, and dynamic text.

Parameters

NameTypeRequiredDescription
dialogtableYesNew dialog structure to display

If no dialog is currently open, nothing happens.


CloseDialog

code
exports['ngd-ui']:CloseDialog()

Closes the currently open dialog and its NPC camera, restoring normal player control.

Use this when a dialog choice transitions to another UI — for example opening a menu (CreateMenu) or shop after the player makes a selection, so the dialog and its camera don't linger behind the new UI.

Parameters

None.

Safe to call when no dialog is open — it simply does nothing.

Example — dialog choice that opens a menu

code
-- A "Buy Drinks" choice asks the server for stock; the server then either updates the
-- dialog (out of stock) or triggers this client event (in stock):
RegisterNetEvent('myresource:Client:OpenStore', function(items)
    exports['ngd-ui']:CloseDialog()          -- dismiss the greeting dialog + camera
    exports['ngd-ui']:CreateMenu(items)      -- then open the store menu
end)

StartDialog

code
exports['ngd-ui']:StartDialog(entity, graph, vars)

Requires ngd-ui 1.49+.

Opens a dialog built from a node graph instead of a single flat dialog table. Rather than chaining UpdateDialog calls by hand inside func callbacks, you describe the whole conversation as a map of nodes up front and let ngd-ui walk between them as the player clicks choices.

Parameters

NameTypeRequiredDescription
entitynumberYesEntity handle (usually a PED/object)
graphtableYesThe dialog graph (start id + nodes table, structure below)
varstableNoKey → value map used to fill {name} placeholders in the graph's text

Graph Format

code
local graph = {
    start = 'greeting',
    typeSpeed = 30, -- optional, ms per character
    nodes = {
        greeting = {
            title       = 'NPC',
            icon        = 'fa-solid fa-user',
            description = 'Hello, {name}! What can I do for you?',
            tags        = { 'Merchant' },
            choices     = {
                { title = 'Tell me about your wares', next = 'shop' },
                { title = 'Goodbye', close = true },
            },
        },
        shop = {
            description = 'Take a look around.',
            choices     = {
                { title = 'Buy a health potion', serverEvent = 'myresource:Server:BuyPotion', args = { item = 'potion' } },
                { title = 'Back', next = 'greeting' },
            },
        },
    },
}

exports['ngd-ui']:StartDialog(ped, graph, { name = GetPlayerName(PlayerId()) })

graph.nodes is a map of node id → node body (not a list), and graph.start names the id to open first. Choices move between nodes with next — there is no goto field.

Node Fields

FieldTypeRequiredDescription
titlestringNoHeader label for this node
iconstringNoFontAwesome icon name
descriptionstringNoBody text
tagsstring[]NoOptional info labels
choicestable[]YesThis node's clickable options (see below)

Choice Fields

FieldTypeRequiredDescription
titlestringYesUI text for the button
nextstringNoId of the node to show when this choice is clicked
clientEventstringNoClient event to trigger (TriggerEvent(clientEvent, args))
serverEventstringNoServer event to trigger (TriggerServerEvent(serverEvent, args))
commandstringNoCommand string to run (ExecuteCommand(command))
argsany/tableNoOptional data passed to clientEvent/serverEvent
closebooleanNoClose the dialog + camera when clicked
hidebooleanNoHide the dialog UI while this choice's actions run

If a choice sets both close and next, close wins — ngd-ui logs an error and ignores next.

Placeholders (vars)

Any {name} token found in a node's title, description, or tags, or in a choice's title, is substituted Lua-side against the vars table passed as StartDialog's third argument — each node is substituted fresh as it's shown, so a next transition re-runs substitution against the same vars. If vars is omitted, or has no entry for a given name, the placeholder is left as literal text ({name}).

Error Behavior

  • If graph isn't a table, or graph.start/graph.nodes are missing, StartDialog logs an error and does nothing — no dialog opens.
  • If a node id doesn't exist in graph.nodes — whether it's graph.start itself or a choice's next target — ngd-ui logs an error naming the missing id. A bad start means the dialog never opens; a bad next means that choice simply doesn't navigate (its other actions, if any, still run) and the current node stays put.

Build dialog graphs visually with the Dialog Builder — drag out the conversation as a flow chart and export ready-to-paste StartDialog code, or import an existing graph back in to keep editing it.


Dialog Structure

A dialog is a Lua table:

code
local dialog = {
    title       = "Character Name",
    icon        = "user",
    description = "Some dialog text here.",
    choices     = { /* choice list */ },
    tags        = { "NPC", "Quest" }
}

Fields

FieldTypeRequiredDescription
titlestringYesHeader label in dialog
iconstringNoFontAwesome icon name
descriptionstringNoBody text
choicestable[]YesAction list
tagsstring[]NoOptional info labels

Choice Structure

Each entry in dialog.choices is a clickable option:

code
{
    title       = "Redeem Ticket",
    shouldClose = false,
    shouldHide  = false,
    clientEvent = "myresource:Client:Redeem",
    serverEvent = "myresource:Server:Redeem",
    command     = "me waves",
    func        = function(args) end,
    args        = { some = "data" }
}

Choice Fields

FieldTypeRequiredDefaultDescription
titlestringYes-UI text for button
shouldClosebooleanNofalseClose dialog after click
shouldHidebooleanNofalseHide UI during function execution
clientEventstringNonilClient event to trigger
serverEventstringNonilServer event to trigger
commandstringNonilCommand string to run
funcfunctionNonilCustom Lua function
argsany/tableNonilOptional data for events/function

Action Order

When a choice is clicked, ngd-ui does:

  1. Calls SetPedTalk(CurrentNPC) (if present in your project).

  2. Reads CurrentDialog.choices[index].

  3. If choice.shouldClose == true, closes the dialog & camera.

  4. If choice.clientEvent is set:

    code
    TriggerEvent(choice.clientEvent, choice.args)
    
  5. If choice.serverEvent is set:

    code
    TriggerServerEvent(choice.serverEvent, choice.args)
    
  6. If choice.command is set:

    code
    ExecuteCommand(choice.command)
    
  7. If choice.func is set:

    • If choice.shouldHide is true, the dialog is hidden first.
    • choice.func(args) is executed.
    • If choice.shouldHide is true, the dialog is shown again afterwards.

Camera Behavior

When TalkToNPC is called:

  1. CurrentNPC is stored.
  2. A camera is placed slightly in front of the NPC.
  3. A scripted camera is created and activated:
    • Focused on the NPC
    • Slightly above and facing them
  4. NUI focus is enabled.

When the dialog is closed (e.g. via shouldClose = true):

  • Scripted camera is destroyed.
  • Script cam rendering stops.
  • NUI focus is released.
  • Internal dialog/NPC references are cleared.

Basic Usage Example

Simple vendor dialog

code
local function OpenVendorDialog(ped)
    exports['ngd-ui']:TalkToNPC(ped, {
        title       = "Ticket Vendor",
        icon        = "ticket",
        description = "Welcome! What would you like to do?",
        tags        = { "Service", "Vendor" },
        choices     = {
            {
                title       = "Redeem Ticket",
                shouldClose = false,
                clientEvent = "myresource:Client:RedeemTicket",
                args        = { type = "standard" }
            },
            {
                title       = "Ask a Question",
                shouldClose = false,
                command     = "me asks a question"
            },
            {
                title       = "Goodbye",
                shouldClose = true
            }
        }
    })
end

You can hook this into a target system or interaction:

code
AddEventHandler("myresource:OpenVendor", function(ped)
    OpenVendorDialog(ped)
end)

Real-World Example

Test PED dialog from ngd-ui

code
local function BuildIntroDialog(ped)
    local serverId = GetPlayerServerId(PlayerId())

    local choices = {}

    -- Option 1: Who are you?
    choices[#choices + 1] = {
        title       = "Who are you?",
        shouldClose = false,
        func        = function()
            exports['ngd-ui']:UpdateDialog({
                title       = "About Me",
                icon        = "user",
                description = "I'm just a test NPC used to demonstrate the ngd-ui dialog system.",
                tags        = { "ngd-ui is cool!" },
                choices     = {
                    {
                        title       = "Back",
                        shouldClose = false,
                        func        = function()
                            exports['ngd-ui']:UpdateDialog(BuildIntroDialog(ped))
                        end
                    },
                    {
                        title       = "Goodbye",
                        shouldClose = true,
                        func        = function()
                            CreateTestPedText()
                        end
                    }
                }
            })
        end
    }

    -- Option 2: What is this UI?
    choices[#choices + 1] = {
        title       = "What is this UI?",
        shouldClose = false,
        func        = function()
            exports['ngd-ui']:UpdateDialog({
                title       = "ngd-ui Dialog",
                icon        = "info-circle",
                description = "This is a demo conversation showing how dialogs can update without closing.",
                tags        = { "Dialog Update Example" },
                choices     = {
                    {
                        title       = "Back",
                        shouldClose = false,
                        func        = function()
                            exports['ngd-ui']:UpdateDialog(BuildIntroDialog(ped))
                        end
                    },
                    {
                        title       = "Goodbye",
                        shouldClose = true,
                        func        = function()
                            CreateTestPedText()
                        end
                    }
                }
            })
        end
    }

    -- Option 3: Goodbye (main menu)
    choices[#choices + 1] = {
        title       = "Goodbye",
        shouldClose = true,
        func        = function()
            CreateTestPedText()
        end
    }

    return {
        title       = "Test NPC",
        icon        = "user",
        description = "Hi there! Choose an option below to see dialog updates in action.",
        tags        = { "TAG Example", "I am just an NPC :(", "Live Variables. Server ID: " .. serverId },
        choices     = choices
    }
end

Triggered with:

code
exports['ngd-ui']:TalkToNPC(testPed, BuildIntroDialog(testPed))

Notes & Best Practices

  1. Always provide a title and at least one choice.
  2. Use UpdateDialog for multi-step flows instead of closing and reopening.
  3. Use shouldHide when you temporarily need to hide the dialog to show another UI.
  4. Prefer server events for game-changing actions (items/money) and client events for local visuals.
  5. Keep descriptions and choice titles concise for readability.



Test Commands (Click to Expand)

Test Commands

Enable test commands by setting Config.TestCommands = true in your configuration.

Camera System

CommandDescription
/testcamerasOpens a test camera system with 3 cameras. Use arrow keys to switch, backspace to exit.

Flipbook

CommandDescription
/testflipbookOpens a test flipbook menu. Use arrow keys or buttons to flip pages, ESC to close.

Menu

CommandDescription
/testmenuOpens a basic context menu with 3 options (button, server event, checkbox).

Input Dialog

CommandDescription
/testinputOpens a basic input dialog with 2 fields (name and age).
/testinputfullOpens a dialog showcasing all input types (text, password, number, date, select, checkbox).

Help Text

CommandDescription
/testhelptextShows vertical help text at bottom center for 5 seconds.
/testhelptexthShows horizontal help text at bottom center for 5 seconds.

3D Text & Interactions

CommandDescription
/testinteractCreates a single interaction point 2m ahead. Press E when in range.
/testmultiCreates a multi-option interaction 2m ahead. Press E or G when in range.
/testmarkerCreates an information text marker 3m ahead.
/cleartestsRemoves all test interactions and markers.

Notification System

CommandDescription
/testnotifyShows a standard notification
/testnotifyallShows all of the notifications.
/testnotifyiconShows an 'advanced' notification with FA icon.

Simple Shops

CommandDescription
/testshopOpens a shop.

Progress Bars

CommandDescription
/testprogressRuns a test progress bar.
/testpcircleRuns a circle test progress bar.
/testppropRuns a progress bar with a prop.

PED Dialog

| Command | Description | Requires | |---------|-------------| | /testdialog | This opens the dialog for the test ped found at coords: 116.36, -1088.41, 28.23 | Config.TestPedInteractions = true

Debug Commands

CommandDescriptionRequires
/drawtextdebugShows debug info for all active 3D text elements, markers, and interactions.Config.ThreeDTextDebug = true

Usage Example

code
-- Enable in your config file
Config.TestCommands = true
Config.ThreeDTextDebug = true

-- In-game
Use the / commands listed above to show examples of each UI element.

Configuration Notes

  • Editable Files
    Please review the editable files in the client and server folder and enter in the correct exports for your servers configuration.

Do you still need help? Open a ticket in our Discord