Placeholders

Placeholders are dynamic values that get replaced at runtime. Use them in templates and commands to show player data.

Built-in Placeholders

Framework Placeholders

These require a framework (qb-core, qbx_core, or es_extended):

Placeholder Returns Example
{Framework:GetName} Character name "Kanuu Overflow"
{Framework.Functions.GetPlayerName} Character name (alt syntax) "Kanuu Overflow"
{Framework:GetJob} Job name "police"
{Framework:GetJobLabel} Job display name "Police Officer"
{Framework:GetJobGrade} Job grade level "1"
{Framework:GetGang} Gang name "ballas"

Masked Placeholders

Show "Anonymous" if player is wearing a mask:

Placeholder Behavior
{Masked:Framework.Functions.GetPlayerName} Real name OR "Anonymous" if masked
{Masked:Framework:GetJob} Job OR "Unknown" if masked

Friends Placeholders

Only show data to friends:

Placeholder Behavior
{Friends:Framework.Functions.GetPlayerName} Real name to friends, "Stranger" to others

Server Placeholders

Placeholder Returns Example
{Placeholder:ServerId} Server ID "42"
{Placeholder:CfxName} CFX username "kanuu #5127"

Self Placeholders

Message sender info:

Placeholder Returns
{Self:GetArgs} All message arguments
{Self:GetFirstArg} First argument only
{Self:GetSource} Server ID of sender

Using Placeholders in Templates

In Static Text

T.text('Player {Placeholder:ServerId}')

In Author Field

T.author()  -- Uses framework name by default
-- Override with custom text:
T.build()
    :author({})
    :text(' ({Placeholder:ServerId})')
    :done()

In Message Content

Placeholders work anywhere in text:

T.message()
-- Shows message with placeholder values

Server-Side Resolution

For dont_register: templates, placeholders are resolved on the server:

Config.Theme['dont_register:job_ad'] = T.build()
    :text('Hiring for {Framework.Functions.GetJobLabel}', { bold = true })
    :message()
    :done()

The server processes {Framework.Functions.GetJobLabel} before sending to clients.

Creating Custom Placeholders

Add custom placeholders in config/placeholders.lua:

Client-Side Placeholder

-- client/custom/placeholder.lua
Client.Custom["Placeholder"]["MyCustom"] = function(placeholder, orTable, args)
    return "Custom value"
end

-- Usage in template:
T.text('Custom: {Placeholder:MyCustom}')

Server-Side Placeholder

-- server/placeholders.lua
Server.Custom["Placeholder"]["MyCustom"] = function(placeholder, orTable, args)
    return "Server value"
end

-- Usage (dont_register template only):
T.text('Custom: {Placeholder:MyCustom}')

Framework-Based Placeholder

Client.Custom["Placeholder"]["Health"] = function(placeholder, orTable, args)
    local health = GetEntityHealth(PlayerPedId())
    return tostring(health)
end

-- Usage:
T.text(' [HP: {Placeholder:Health}]')

With Arguments

Client.Custom["Placeholder"]["JobGrade"] = function(placeholder, orTable, args)
    -- args contains information from the placeholder itself
    local framework = Framework.Functions.GetPlayerData()
    return tostring(framework.job.grade.level or 0)
end

Example: Complete Custom Placeholder

-- In config/placeholders.lua

Client.Custom["Placeholder"]["Distance"] = function(placeholder, orTable, args)
    -- Get current player position
    local ped = PlayerPedId()
    local playerCoords = GetEntityCoords(ped)
    
    -- Get target coords from args (if provided)
    -- This would be passed from the command handler
    if args and args[1] then
        local targetCoords = json.decode(args[1])
        local distance = #(playerCoords - targetCoords)
        return string.format("%.1f m", distance)
    end
    
    return "N/A"
end

-- Usage:
Commands.client("distance", {
    handler = function(ctx)
        local playerCoords = GetEntityCoords(PlayerPedId())
        TriggerEvent('chat:addMessage', {
            template = HTML.createTemplate(T.build()
                :tag('DISTANCE')
                :text(': {Placeholder:Distance}')
                :done()),
            args = { json.encode(playerCoords) }
        })
    end,
})

Conditional Placeholders

For complex logic, use buildText in commands:

Commands.client("status", {
    handler = function(ctx)
        local health = GetEntityHealth(PlayerPedId())
        local healthText = health > 100 and "Healthy" or "Injured"
        
        TriggerEvent('chat:addMessage', {
            templateId = 'cmd:me',
            args = { ctx.author, "is " .. healthText }
        })
    end,
})

Placeholder Syntax

Different placeholder types:

{Framework:GetName}                    -- Framework method
{Framework.Functions.GetPlayerName}    -- Nested functions
{Placeholder:ServerId}                 -- Placeholder group
{Masked:Framework:GetJob}             -- Masked modifier
{Friends:Framework:GetName}           -- Friends modifier
{Self:GetArgs}                        -- Self reference

Escaping Placeholders

Prevent placeholder substitution by using different syntax:

-- This WILL be replaced:
T.text('Hello {Framework:GetName}')

-- To prevent, you must remove the placeholder:
T.text('Hello World')

Templates
../templates/
Commands
../commands/