Sending Messages from Other Scripts

How to send chat messages from your custom scripts.

Basic Usage

Simple Message

TriggerEvent('chat:addMessage', {
    args = { "Hello World" }
})

With Author

TriggerEvent('chat:addMessage', {
    args = { "PlayerName", "Hello World" }
})

Using Templates

Registered Template

-- Use a template from Config.Theme
TriggerEvent('chat:addMessage', {
    templateId = 'cmd:me',
    args = { GetPlayerName(PlayerId()), "does something" }
})

Built-in Presets

TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(T.Presets.me()),
    args = { GetPlayerName(PlayerId()), "waves" }
})

TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(T.Presets.announce('INFO', '#2ecc71', 'rgba(46,204,113,0.12)')),
    args = { "Server announcement here" }
})

TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(T.Presets.ooc()),
    args = { GetPlayerName(PlayerId()), "meta chat" }
})

Getting the Theme Builder

Before building custom templates, get the theme builder export:

local T = exports['chat']:getThemeBuilder()

Custom Inline Template

TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(
        T.build()
            :bg('#1a1a2e')
            :tag('CUSTOM', '#00d4ff')
            :author({ bold = true })
            :text(': ')
            :message()
            :done()
    ),
    args = { GetPlayerName(PlayerId()), "Custom message" }
})

From Server Scripts

Broadcast to All

-- server/my_script.lua
TriggerClientEvent('chat:addMessage', -1, {
    templateId = 'announce',
    args = { "Server message to everyone" }
})

Send to Specific Player

TriggerClientEvent('chat:addMessage', targetPlayerId, {
    templateId = 'cmd:msg',
    args = { "Admin", "You've been warned" }
})

Send to Group

local onlinePlayers = GetPlayers()
for _, playerId in ipairs(onlinePlayers) do
    TriggerClientEvent('chat:addMessage', playerId, {
        templateId = 'announce',
        args = { "Message to all online players" }
    })
end

Server-Side Template Resolution

For dont_register: templates, resolve on server:

-- config/templates.lua
Config.Theme['dont_register:job_ad'] = T.build()
    :bg('#f39c12')
    :text('Job Opening - {Framework.Functions.GetJobLabel}', {
        bold = true,
        color = '#1a1a1a'
    })
    :message()
    :done()

-- server/job_ads.lua
TriggerClientEvent('chat:addMessage', -1, {
    template = resolvedHtml,  -- Already processed server-side
    args = { "Looking for police officers" }
})

Channels

Send to Channel

TriggerEvent('chat:addMessage', {
    templateId = 'cmd:me',
    channel = 'police',     -- Only police channel members see
    args = { GetPlayerName(PlayerId()), "radio message" }
})

Channel Defaults

Commands automatically use their configured channel:

Config.Commands.twitter = {
    command = "twitter",
    channel = "twitter",  -- Uses this channel
}

-- Client-side (auto-channels)
Commands.sendAll('twitter', { GetPlayerName(PlayerId()), message })

Component Argument Mapping

Components consume args sequentially:

local template = T.build()
    :tag('TEST')          -- Consumes nothing
    :author()             -- ← consumes args[1]
    :text(': ')           -- Consumes nothing
    :message()            -- ← consumes args[2]
    :spacer()             -- Consumes nothing
    :text('Extra info')   -- Consumes nothing
    :done()

-- args[1] = "PlayerName"
-- args[2] = "This is the message"
TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(template),
    args = { "PlayerName", "This is the message" }
})

Advanced Examples

Job Announcement

-- server/job_ads.lua
function AnnounceJobOpening(job, message)
    local jobLabel = Framework.Functions.GetJobLabel(job)
    
    TriggerClientEvent('chat:addMessage', -1, {
        templateId = 'announce:job',
        args = { jobLabel .. " - " .. message }
    })
end

-- Usage:
AnnounceJobOpening('police', 'Looking for recruits')

Player Action

-- client/my_action.lua
local function PerformAction(action, success)
    local template = success and 'cmd:try:success' or 'cmd:try:fail'
    
    TriggerEvent('chat:addMessage', {
        templateId = template,
        args = { GetPlayerName(PlayerId()), action }
    })
end

PerformAction('to pick the lock', true)  -- Shows success
PerformAction('to open the safe', false) -- Shows fail

Private Message

-- server/messages.lua
function SendPrivateMessage(fromId, toId, message)
    local fromName = Framework.Functions.GetPlayerName(fromId)
    
    TriggerClientEvent('chat:addMessage', toId, {
        templateId = 'cmd:msg',
        args = { fromName, message }
    })
end

System Notification

TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(
        T.Presets.system('SYSTEM', '#95a5a6', 'rgba(149, 165, 166, 0.12)')
    ),
    args = { "Server is restarting in 5 minutes" }
})

Dispatch Message

TriggerClientEvent('chat:addMessage', -1, {
    template = HTML.createTemplate(
        T.Presets.announce('DISPATCH', '#e74c3c', 'rgba(231, 76, 60, 0.12)')
    ),
    args = { "Traffic incident at location" }
})

Multi-Line Message

TriggerEvent('chat:addMessage', {
    args = { "Admin", "Line 1\nLine 2\nLine 3" }
})

Exports

Use exports instead of events:

-- Send via export
exports['chat']:addMessage({
    args = { "Player", "Message" }
})

-- From server
exports['chat']:sendAll({
    templateId = 'announce',
    args = { "Announcement" }
})

Important Notes

Args Consumption

Not all components consume args:

Consumes args:

  • T.author() - uses next arg
  • T.message() - uses next arg

Does NOT consume args:

  • T.tag(text) - static
  • T.text(content) - static
  • T.icon(svg) - static
  • T.spacer() - static
  • T.bold(), T.italic() - static

Placeholder Processing

  • Client templates: placeholders replaced on client
  • Server templates (dont_register:): placeholders replaced on server
  • Framework values: requires framework to be loaded

Template ID vs Template

  • templateId - Use registered template from Config.Theme
  • template - Inline template (must use HTML.createTemplate())

Don't mix them:

-- ✓ Correct
TriggerEvent('chat:addMessage', {
    templateId = 'cmd:me',  -- Uses Config.Theme['cmd:me']
    args = { ... }
})

-- ✓ Correct
TriggerEvent('chat:addMessage', {
    template = HTML.createTemplate(T.build()...),  -- Inline
    args = { ... }
})

-- ✗ Wrong - don't use both
TriggerEvent('chat:addMessage', {
    templateId = 'cmd:me',
    template = HTML.createTemplate(...),  -- Conflict!
    args = { ... }
})

Backward Compatibility

Still works (legacy format):

TriggerEvent('chatMessage', 'PlayerName', 'msg', message)

But presets are recommended for new code.

Templates
../templates/
Commands
../commands/
Configuration
../configuration/