RLS Studios
ProjectsPatreonCommunityDocsAbout
Join Patreon
BeamNG Modding Docs

Guides

Reference

UI

UI Apps - HUD WidgetsBridge/API Layer - Lua ↔ UI CommunicationBeamNG UI Development Cheat SheetCommon Components - Bng* UI ElementsLua ↔ UI Integration - Full Round-Trip PatternsServices - Shared UtilitiesVue Modules - Full-Page Screens

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

UI Framework

BeamNG UI Development Cheat Sheet

40+ high-frequency one-liners and snippets for UI development.

40+ high-frequency one-liners and snippets for UI development.


1. Event Handling

TaskCode
Listen for eventevents.on('EventName', (data) => { ... })
Listen onceevents.once('EventName', (data) => { ... })
Remove listenerevents.off('EventName', handler)
Emit eventevents.emit('CustomEvent', data)
Auto-cleanup eventsconst events = useEvents()
// Setup
import { useEvents } from '@/services/events'
const events = useEvents()
events.on('VehicleChange', () => { /* ... */ })

2. Stream Subscriptions

TaskCode
Subscribe to streamsuseStreams(['electrics'], handler)
Multiple streamsuseStreams(['electrics', 'sensors'], handler)
Manual subscribe$game.streams.add(['electrics'])
Manual unsubscribe$game.streams.remove(['electrics'])
Get speedstreams.electrics?.wheelspeed
Get RPMstreams.electrics?.rpm
Get gearstreams.electrics?.gear
Get yawstreams.sensors?.yaw
// Setup
import { useStreams } from '@/services/events'

useStreams(['electrics'], (streams) => {
  if (streams.electrics) {
    speed.value = streams.electrics.wheelspeed * 3.6  // km/h
  }
})

3. Lua Calls

TaskCode
Call extensionawait lua.myMod_myExtension.myFunc()
With paramsawait lua.myExt.func(arg1, arg2)
Get vehicle detailsawait lua.core_vehicles.getCurrentVehicleDetails()
Check career activeawait lua.career_career.isActive()
Raw Luaawait runRaw('print("hi")')
Vehicle Luaapi.activeObjectLua('electrics.setIgnitionLevel(2)')
// Setup
import { lua } from '@/bridge'
import { runRaw } from '@/bridge/libs/Lua.js'

const data = await lua.myExtension.getData()

4. Components

ComponentUsage
Button<BngButton @click="fn">Text</BngButton>
Icon Button<BngButton :icon="icons.save" />
Primary Button<BngButton :accent="ACCENTS.primary">
Icon<BngIcon type="engine" />
Input<BngInput v-model="text" />
Slider<BngSlider v-model="val" :min="0" :max="100" />
Switch<BngSwitch v-model="enabled" />
Dropdown<BngDropdown v-model="sel" :items="opts" />
Card<BngCard>Content</BngCard>
Binding<BngBinding ui-event="back" />
// Import
import { BngButton, BngIcon, ACCENTS, icons } from '@/common/components/base'

5. Directives

DirectiveUsage
Blur backgroundv-bng-blur
Tooltipv-bng-tooltip="'Text'"
Tooltip topv-bng-tooltip:top="'Text'"
Disabledv-bng-disabled="!ready"
UI Nav handlerv-bng-on-ui-nav:back="goBack"
Scoped navv-bng-scoped-nav="{ activateOnMount: true }"
Focus ifv-bng-focus-if="shouldFocus"
// Import
import { vBngBlur, vBngTooltip, vBngOnUiNav } from '@/common/directives'

6. Popups

TaskCode
Messageawait openMessage('Title', 'Message')
Confirmawait openConfirmation('Title', 'Message')
Promptawait openPrompt('Enter name:', 'Title')
ProgressopenProgress('Loading...', 'Title')
// Import
import { openMessage, openConfirmation, openPrompt } from '@/services/popup'

const confirmed = await openConfirmation('Confirm', 'Are you sure?')
if (confirmed) { /* ... */ }

7. Translation

TaskCode
Instant translate$translate.instant('ui.common.okay')
Template{{ $t('ui.common.okay') }}
Context translate$translate.contextTranslate({txt: 'key', context: {}})
Template context{{ $ctx_t({txt: 'key', context: {}}) }}
// Import
import { $translate } from '@/services'

const text = $translate.instant('ui.common.confirm')

8. Navigation

TaskCode
Push routerouter.push('/path')
Named routerouter.push({ name: 'myRoute' })
With paramsrouter.push({ name: 'route', params: { id: '1' } })
Go backrouter.back()
Angular statebngVue.gotoAngularState('menu.mainmenu')
Game statebngVue.gotoGameState('garagemode')
// Import
import { useRouter } from 'vue-router'
const router = useRouter()

router.push({ name: 'vehicleInventory' })

9. Storage

TaskCode
Create storagenew Storage('myApp')
Get valuestorage.get('key', default)
Set valuestorage.set('key', value)
Check existsstorage.has('key')
Deletestorage.del('key')
Reactive valuesstorage.values.key
// Import
import Storage from '@/services/storage'

const storage = new Storage('myApp', { theme: 'dark' })
storage.values.theme = 'light'  // Auto-saves

10. App Structure

<!-- Minimal UI App -->
<template>
  <div class="my-app">{{ data }}</div>
</template>

<script setup>
import { ref } from 'vue'
import { useStreams } from '@/services/events'

const data = ref(0)

useStreams(['electrics'], (s) => {
  if (s.electrics) data.value = s.electrics.wheelspeed
})
</script>

<style scoped>
.my-app {
  background: rgba(0,0,0,0.5);
  color: white;
}
</style>

11. Route Structure

// routes.js
import MyView from './views/MyView.vue'

export default [
  {
    path: '/myModule',
    name: 'myModule',
    component: MyView,
    meta: {
      uiApps: { shown: false },
      infoBar: { visible: true }
    }
  }
]

12. Common Stream Data

// electrics
streams.electrics.wheelspeed    // m/s
streams.electrics.rpm           // RPM
streams.electrics.gear          // gear number
streams.electrics.fuel          // 0-1
streams.electrics.lowbeam       // boolean
streams.electrics.ignitionLevel // 0-2

// sensors
streams.sensors.yaw             // radians
streams.sensors.pitch           // radians
streams.sensors.roll            // radians
streams.sensors.gx              // G-force
streams.sensors.gy
streams.sensors.gz

13. Common Events

EventDataPurpose
VehicleChange-Vehicle switched
VehicleFocusChanged{mode}Camera changed
ChangeState{state}Navigate
Message{text,icon,ttl}HUD message
toastrMsg{type,title,msg}Toast
MenuHide-Close menu

14. Common Lua Calls

// Career
await lua.career_career.isActive()
await lua.career_modules_inventory.sendDataToUi()
await lua.career_modules_playerAttributes.getAttribute('money')

// Vehicles
await lua.core_vehicles.getCurrentVehicleDetails()
await lua.core_vehicles.getModel()

// Extensions
await lua.extensions.load('myExtension')
await lua.extensions.myExtension.myFunction()

// Garage
await lua.extensions.gameplay_garageMode.setGarageMenuState('tuning')

15. CSS Patterns

// Common variables
color: var(--bng-off-white);
background: rgba(var(--bng-off-black-rgb), 0.5);
border-radius: var(--bng-corners-1);
font-family: var(--fnt-defs);

// Icon sizing
.icon {
  --bng-icon-size: 2em;
  --bng-icon-color: var(--bng-orange);
}

// Full screen layout
.my-view {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: 2em;
}

16. Quick Imports

// Components
import { BngButton, BngIcon, BngInput, ACCENTS, icons } from '@/common/components/base'

// Directives
import { vBngBlur, vBngTooltip, vBngOnUiNav, vBngDisabled } from '@/common/directives'

// Services
import { useEvents, useStreams } from '@/services/events'
import { openMessage, openConfirmation } from '@/services/popup'
import { $translate } from '@/services'
import Storage from '@/services/storage'

// Bridge
import { lua } from '@/bridge'
import { useBridge } from '@/bridge'
import { runRaw } from '@/bridge/libs/Lua.js'

// Router
import { useRouter, useRoute } from 'vue-router'

17. Lifecycle Pattern

import { onMounted, onUnmounted, ref } from 'vue'
import { useEvents, useStreams } from '@/services/events'
import { lua } from '@/bridge'

const data = ref(null)
const events = useEvents()

onMounted(async () => {
  // Fetch initial data
  data.value = await lua.myExtension.getData()
  
  // Listen for updates
  events.on('DataUpdated', (d) => { data.value = d })
})

// Streams auto-cleanup with useStreams()
useStreams(['electrics'], (s) => { /* ... */ })

// Events auto-cleanup with useEvents()

18. Error Handling

async function safeLuaCall() {
  try {
    const result = await lua.myExtension.riskyFunction()
    return result
  } catch (e) {
    console.error('Lua call failed:', e)
    await openMessage('Error', 'Operation failed')
    return null
  }
}

See Also

  • UI Apps - Full app documentation
  • Components - All Bng* components
  • Bridge - Lua communication details
  • Services - All service APIs

Bridge/API Layer - Lua ↔ UI Communication

The bridge layer connects JavaScript UI code to the Lua game engine. It provides event handling, stream subscriptions, and typed Lua function calls.

Common Components - Bng* UI Elements

Reusable Vue components and directives for building BeamNG UI.

On this page

1. Event Handling2. Stream Subscriptions3. Lua Calls4. Components5. Directives6. Popups7. Translation8. Navigation9. Storage10. App Structure11. Route Structure12. Common Stream Data13. Common Events14. Common Lua Calls15. CSS Patterns16. Quick Imports17. Lifecycle Pattern18. Error HandlingSee Also