RLS Studios
ProjectsPatreonCommunityDocsAbout
Join Patreon
BeamNG Modding Docs

Guides

Creating HUD AppsLua ↔ UI BridgeGUI Hooks Reference

Reference

UI

Resources

BeamNG Game Engine Lua Cheat SheetGE Developer RecipesMCP Server Setup

// RLS.STUDIOS=true

Premium Mods for BeamNG.drive. Career systems, custom vehicles, and immersive gameplay experiences.

Index

HomeProjectsPatreon

Socials

DiscordPatreon (RLS)Patreon (Vehicles)

© 2026 RLS Studios. All rights reserved.

Modding since 2024

GuidesUI

GUI Hooks Reference

Complete reference for guihooks — sending events to the UI, toast notifications, streaming data, state changes, and common UI event patterns.

guihooks is how your Lua code talks to the UI. Whether you're showing a notification, updating a dashboard, or navigating to a new screen, it all goes through this bridge. This reference covers every method and the most common event patterns.


Overview

guihooks is the primary interface for communicating from GE Lua to the HTML/JavaScript UI. It supports one-way events, request/response patterns, and high-frequency data streaming.

MethodDirectionPattern
guihooks.trigger(event, ...)GE → UIFire-and-forget event
guihooks.triggerClient(targetId, event, ...)GE → specific UITargeted event
guihooks.triggerStream(name, data)GE → UIImmediate stream send
guihooks.queueStream(key, value)GE → UI (batched)High-frequency data
guihooks.sendStreams()GE → UIFlush queued streams
guihooks.message(msg, ttl, category, icon)GE → UISimple popup message
guihooks.triggerRawJS(event, jsonStr)GE → UIPre-formatted JSON event

Triggering Events

Basic Events

-- One-way event to UI - most common pattern
guihooks.trigger('EventName', data)

-- String data
guihooks.trigger('MenuOpenModule', 'vehicleselect')

-- Table data (auto-serialized to JSON)
guihooks.trigger('MissionStateChanged', {active = true, id = "mission_01"})

-- No data
guihooks.trigger('MenuHide')
guihooks.trigger('ClearTasklist')

State Changes

-- Navigate to a UI state
guihooks.trigger('ChangeState', {state = 'play', params = {}})
guihooks.trigger('ChangeState', {state = 'menu'})
guihooks.trigger('ChangeState', {state = 'vehicleInventory'})
guihooks.trigger('ChangeState', {state = 'painting', params = {}})
guihooks.trigger('ChangeState', {state = 'tuning', params = {}})

-- Navigate back
guihooks.trigger('UINavigation', 'back', 1)

Notifications (Toast Messages)

-- Standard notification
guihooks.trigger('Notification', {
    type = 'info',      -- 'info', 'warning', 'error', 'success'
    title = 'Title',
    message = 'Description text'
})

-- Toast-style (shorter, less intrusive)
guihooks.trigger("toastrMsg", {
    type = "success",
    title = "Game Saved",
    msg = ""
})

guihooks.trigger("toastrMsg", {
    type = "error",
    title = "Update required",
    msg = "Please update to the latest version."
})

guihooks.trigger("toastrMsg", {
    type = "info",
    title = "Vehicle sold",
    msg = vehicleInfo.Name .. " for $" .. string.format("%.2f", price)
})

guihooks.trigger("toastrMsg", {
    type = "warning",
    label = "vehStored",
    title = "Vehicle stored",
    msg = "Damaged vehicles have been moved to storage."
})

Data Responses

Send data back to the UI in response to UI-initiated queries:

-- Respond with career save slots
guihooks.trigger("allCareerSaveSlots", resultTable)

-- Respond with vehicle inventory data
guihooks.trigger("vehicleInventoryData", data)

-- Respond with purchase data
guihooks.trigger("vehiclePurchaseData", data)

-- Respond with current save slot name
guihooks.trigger("currentSaveSlotName", {saveSlot = slotName})

-- Respond with shopping cart data
guihooks.trigger("sendPaintingShoppingCartData", data)
guihooks.trigger("sendTuningShoppingData", shoppingData)

-- Signal completion
guihooks.trigger("saveFinished")
guihooks.trigger("onCareerPaintingStarted")

Streaming Data

For high-frequency data (e.g., telemetry, live dashboards):

Queued Streams (Batched)

-- Queue data for batch send (call in onUpdate)
local function onUpdate(dtReal, dtSim, dtRaw)
    guihooks.queueStream('vehicleSpeed', currentSpeed)
    guihooks.queueStream('engineRPM', currentRPM)
    guihooks.queueStream('fuelLevel', fuelPercent)
    -- Streams are flushed automatically by main.lua's render loop
end
M.onUpdate = onUpdate

Immediate Streams

-- Send stream data immediately (bypass queue)
guihooks.triggerStream('missionProgress', {
    completed = 3,
    total = 10,
    currentObjective = "Deliver package"
})

Common UI Events Reference

Game State Events

EventDataPurpose
ChangeState{state = 'play'}Navigate UI to state
MenuOpenModulemoduleName (string)Open UI module
MenuHide-Hide menu overlay
UINavigation'back', countNavigate back
HideCareerTasklist-Hide career task UI
ClearTasklist-Clear task list

Notification Events

EventDataPurpose
Notification{type, title, message}Full notification
toastrMsg{type, title, msg}Toast message

Career Events

EventDataPurpose
allCareerSaveSlotstableSave slot list
vehicleInventoryDatatableInventory data
vehiclePurchaseDatatablePurchase info
vehicleShopDeltatableShop changes
gameContextPlayerVehicleDamageInfo{needsRepair}Damage status
onAnyMissionChangedstate, missionIdMission state change
saveFinished-Save completed
addListing{inventoryId}Add marketplace listing

Vehicle/Tuning Events

EventDataPurpose
onCareerPaintingStarted-Paint mode entered
sendPaintingShoppingCartDatatablePaint shop data
sendTuningShoppingDatatableTuning shop data
updateTuningVariabletableTuning var update

Common Patterns

State + UI Sync (No Ghost Values!)

local isActive = false

-- Always update UI when state changes
local function setActive(active)
    isActive = active
    guihooks.trigger('MyModStateChanged', {active = isActive})
end

-- DON'T do this (ghost value - UI out of sync):
-- isActive = true
-- (forgot to trigger UI update)

Confirm Dialog Pattern

-- Show confirmation then execute
guihooks.trigger('ConfirmDialog', {
    title = "Are you sure?",
    message = "This will delete your save.",
    confirmLabel = "Delete",
    cancelLabel = "Cancel",
    onConfirm = "myExtension.confirmDelete()",
    onCancel = "myExtension.cancelDelete()"
})

Progressive UI Updates

local isActive = false
local uiTimer = 0

local function onUpdate(dtReal, dtSim, dtRaw)
    if not isActive then return end

    uiTimer = uiTimer + dtReal
    if uiTimer > 0.1 then  -- 10Hz UI update
        uiTimer = 0
        guihooks.trigger('MyModUpdate', getState())
    end
end

M.onUpdate = onUpdate

Reload UI

-- Force full UI reload (after mounting/unmounting files)
reloadUI()

guihooks.trigger vs be:queueJS

Featureguihooks.triggerbe:queueJS
SerializationAutomatic (JSON)Manual
SafetyHighLow (raw JS)
Use caseStandard eventsLow-level JS calls
RecommendationPrefer thisAvoid unless needed
-- Prefer this:
guihooks.trigger('MyEvent', {key = "value"})

-- Avoid this (raw JavaScript):
be:queueJS("angular.element(document).scope().$broadcast('MyEvent', {key: 'value'})")

Gotchas

  1. Events are async - UI receives them next frame, not immediately.
  2. Data is JSON-serialized - Functions, metatables, and circular refs will be lost.
  3. No confirmation of delivery - trigger is fire-and-forget. Use a callback pattern (VE→GE or UI→GE) if you need a response.
  4. UI must be loaded - Events sent before UI initialization are dropped silently.
  5. String vs table data - Some events expect a string, others a table. Check existing usage.
  6. reloadUI() is expensive - Only call when necessary (e.g., after mounting filesystem changes).

Quick Reference

-- One-way events
guihooks.trigger('EventName', data)
guihooks.trigger('ChangeState', {state = 'play'})
guihooks.trigger('toastrMsg', {type="info", title="Hi", msg="Hello"})

-- Simple message popup
guihooks.message("Hello!", 5, "info")

-- Targeted event (specific UI window)
guihooks.triggerClient(targetDsmId, 'EventName', data)

-- Streaming (high frequency)
guihooks.queueStream('key', value)
guihooks.sendStreams()
guihooks.triggerStream('name', data)

-- Reload
reloadUI()

See Also

  • Cross-VM Comms - Full cross-VM communication patterns
  • UI Bridge - Lua to UI communication details
  • UI Apps - Creating HUD widgets
  • Common Patterns - UI data flow pattern

Lua ↔ UI Bridge

How Lua communicates with the HTML/JavaScript UI layer — events, streams, engine calls, toast messages, and the fade screen system.

Vehicle Control from GE

How to find, spawn, switch, and send commands to vehicles from Game Engine Lua — the essential operations for any gameplay mod.

On this page

OverviewTriggering EventsBasic EventsState ChangesNotifications (Toast Messages)Data ResponsesStreaming DataQueued Streams (Batched)Immediate StreamsCommon UI Events ReferenceGame State EventsNotification EventsCareer EventsVehicle/Tuning EventsCommon PatternsState + UI Sync (No Ghost Values!)Confirm Dialog PatternProgressive UI UpdatesReload UIguihooks.trigger vs be:queueJSGotchasQuick ReferenceSee Also