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

Services - Shared Utilities

Services provide shared functionality across the UI: event handling, popups, translations, storage, and navigation.

Services provide shared functionality across the UI: event handling, popups, translations, storage, and navigation.


Events Service

Provides auto-cleanup event handling tied to component lifecycle.

useEvents Composable

import { useEvents } from '@/services/events'

// Creates event handler that auto-cleans on unmount
const events = useEvents()

// Subscribe to events
events.on('MyEvent', (data) => {
  console.log('Received:', data)
})

// One-time listener
events.once('InitComplete', () => {
  console.log('Initialization done')
})

// Manual unsubscribe (rarely needed)
events.off('MyEvent', specificHandler)

// Emit events (UI → UI)
events.emit('CustomEvent', { data: 'value' })

useStreams Composable

import { useStreams } from '@/services/events'

// Subscribe to streams (auto-cleanup)
useStreams(['electrics', 'sensors'], (streams) => {
  if (streams.electrics) {
    speed.value = streams.electrics.wheelspeed
  }
})

// With manual control
const streamControl = useStreams(['electrics'], handler)
streamControl.off()  // Manually unsubscribe
streamControl.on()   // Re-subscribe

Popup Service

Display modal dialogs, confirmations, and prompts.

Import

import { 
  openMessage,
  openConfirmation,
  openPrompt,
  openProgress,
  addPopup,
  popupsView 
} from '@/services/popup'

Simple Message

import { openMessage } from '@/services/popup'

// Display message with OK button
await openMessage('Title', 'This is a message')

Confirmation Dialog

import { openConfirmation } from '@/services/popup'
import { $translate } from '@/services'
import { ACCENTS } from '@/common/components/base'

const confirmed = await openConfirmation(
  $translate.instant('ui.common.confirm'),
  'Are you sure you want to proceed?',
  [
    { label: 'Yes', value: true, extras: { default: true } },
    { label: 'No', value: false, extras: { cancel: true, accent: ACCENTS.secondary } }
  ]
)

if (confirmed) {
  // User clicked Yes
}

Text Prompt

import { openPrompt } from '@/services/popup'

const name = await openPrompt(
  'Enter your name:',           // Message
  'Name Input',                  // Title
  {
    defaultValue: '',
    maxLength: 50,
    validate: (text) => text.length > 0,
    errorMessage: 'Name is required',
    disableWhenInvalid: true
  }
)

if (name) {
  console.log('User entered:', name)
}

Progress Dialog

import { openProgress } from '@/services/popup'

const popup = openProgress(
  'Downloading...',              // Message
  'Download Progress',           // Title
  {
    indeterminate: false,
    min: 0,
    max: 100,
    initialValue: 0
  }
)

// Update progress
popup.progress.update(50, 'Halfway there...')

// Complete
popup.progress.done(100)

Custom Popup

import { addPopup, PopupTypes } from '@/services/popup'
import MyCustomComponent from './MyCustomComponent.vue'

const popup = addPopup(
  MyCustomComponent,
  { customProp: 'value' },
  PopupTypes.normal
)

// Close from outside
popup.return(result)

// Or from inside component
// props.__popup.return(result)

Translation Service

Internationalization support.

Basic Usage

import { $translate } from '@/services'

// Simple translation
const text = $translate.instant('ui.common.okay')

// In templates
<span>{{ $t('ui.common.okay') }}</span>

Context Translation

For strings with parameters:

import { $translate } from '@/services'

// Object with context
$translate.contextTranslate({
  txt: 'ui.career.moneyFormat',
  context: { amount: 1500 }
})
// Returns: "$1,500" (based on translation file)

// In templates
<span>{{ $ctx_t({ txt: 'ui.career.moneyFormat', context: { amount: 1500 } }) }}</span>

Multi-Context Translation

import { $translate } from '@/services'

// Array of translation objects
$translate.multiContextTranslate([
  { txt: 'ui.career.vehicle' },
  { txt: 'ui.common.colon' },
  { txt: 'ui.vehicles.etk800' }
])

Storage Service

localStorage wrapper with reactivity.

Basic Usage

import Storage from '@/services/storage'

const storage = new Storage('my-app')

// Get/Set
storage.set('name', 'John')
const name = storage.get('name')  // 'John'
const age = storage.get('age', 25)  // 25 (default)

// Check/Delete
storage.has('name')  // true
storage.del('name')

Reactive Values

import Storage from '@/services/storage'

const storage = new Storage('my-app', {
  theme: 'dark',
  volume: 0.8,
  lastPlayed: () => Date.now()  // Function defaults are evaluated when needed
})

// Reactive object
storage.values.theme  // 'dark'
storage.values.volume = 0.5  // Saves to localStorage

// Use in Vue
<template>
  <span>{{ storage.values.theme }}</span>
</template>

Navigator Service

Page and menu navigation.

Basic Navigation

import { useRouter } from 'vue-router'

const router = useRouter()

// Navigate to route
router.push('/career/vehicleInventory')

// Named route with params
router.push({ 
  name: 'vehiclePurchase', 
  params: { vehicleInfo: 'abc123' } 
})

// Go back
router.back()

Game State Navigation

// For Angular/legacy states
const bngVue = window.bngVue

bngVue.gotoAngularState('menu.mainmenu')
bngVue.gotoGameState('garagemode')
bngVue.gotoGameState('menu.vehicles', {
  params: { mode: 'garageMode' }
})

From Lua

-- Navigate to UI state
guihooks.trigger('ChangeState', {state = 'career/vehicleInventory'})

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

UI Apps Service

Control HUD app visibility and layout.

import { useUIApps } from '@/services/uiApps'

const appsAPI = useUIApps()

// Set app layout
appsAPI.setLayout('blank')      // Hide all
appsAPI.setLayout('tasklist')   // Show only tasklist

// Set visibility
appsAPI.setVisible(true)
appsAPI.setVisible(false)

// Get current layout
const layout = appsAPI.currentLayout

Controls Service

Input device detection.

import useControls from '@/services/controls'
import { storeToRefs } from 'pinia'

const controls = useControls()
const { showIfController } = storeToRefs(controls)

// In template
<BngBinding v-if="showIfController" ui-event="back" controller />

Info Bar Service

Bottom information bar.

import { useInfoBar } from '@/services/infoBar'

const infoBar = useInfoBar()

// Control visibility
infoBar.visible = true
infoBar.showSysInfo = true

// Add action hints
infoBar.clearHints()
infoBar.addHints([
  { action: 'confirm', label: 'ui.common.select' },
  { action: 'back', label: 'ui.common.back' }
])

Top Bar Service

Top navigation bar.

import { useTopBar } from '@/services/topBar'

const topBar = useTopBar()

topBar.show()
topBar.hide()

Screen Cover Service

Loading screen control.

import { loadingScreen, startLoading } from '@/services/screenCover'

// Show loading screen
startLoading()

// Check state
if (loadingScreen.shown) {
  // Loading screen is visible
}

Content Services

Content parsing utilities.

import { $content } from '@/services'

// BBCode parsing
$content.bbcode  // BBCode parse utilities

// Markdown parsing
$content.markdown  // Markdown utilities

Note: $content only exports bbcode and markdown modules. There are no vehicles, levels, or parts utilities here.


Common Patterns

Service in Component

<template>
  <div>
    <BngButton @click="confirmDelete">
      {{ $t('ui.common.delete') }}
    </BngButton>
  </div>
</template>

<script setup>
import { openConfirmation } from '@/services/popup'
import { $translate } from '@/services'
import { useEvents } from '@/services/events'
import { ACCENTS } from '@/common/components/base'

const events = useEvents()

async function confirmDelete() {
  const confirmed = await openConfirmation(
    $translate.instant('ui.common.confirm'),
    $translate.instant('ui.myModule.deleteWarning'),
    [
      { label: $translate.instant('ui.common.delete'), value: true },
      { label: $translate.instant('ui.common.cancel'), value: false, extras: { accent: ACCENTS.secondary } }
    ]
  )
  
  if (confirmed) {
    events.emit('ItemDeleted')
  }
}
</script>

Persistent Settings

import Storage from '@/services/storage'

const settings = new Storage('myApp-settings', {
  volume: 0.8,
  showHints: true,
  lastVehicle: null
})

// Read
const vol = settings.values.volume

// Write (auto-saves)
settings.values.volume = 0.5

Event-Driven Updates

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

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

onMounted(() => {
  // Request data
  lua.myExtension.requestData()
  
  // Listen for response
  events.on('DataReceived', (received) => {
    data.value = received
  })
})

Service Imports Summary

// From @/services
import { $translate, useLibStore, $content } from '@/services'

// From @/services/events
import { useEvents, useStreams } from '@/services/events'

// From @/services/popup
import { openMessage, openConfirmation, openPrompt, openProgress, addPopup } from '@/services/popup'

// From @/services/storage
import Storage from '@/services/storage'

// From @/services/uiApps
import { useUIApps } from '@/services/uiApps'

// From @/services/infoBar
import { useInfoBar } from '@/services/infoBar'

// From @/services/topBar
import { useTopBar } from '@/services/topBar'

See Also

  • Bridge API - Low-level event/stream handling
  • Components - Bng* UI components
  • Lua Integration - Full round-trip patterns

Lua ↔ UI Integration - Full Round-Trip Patterns

Complete patterns for bidirectional communication between Lua game engine and Vue UI.

Vue Modules - Full-Page Screens

Vue Modules are full-page screens like the career menu, garage, vehicle config, and bigmap. They use Vue Router for navigation and can contain complex multi-view layouts.

On this page

Events ServiceuseEvents ComposableuseStreams ComposablePopup ServiceImportSimple MessageConfirmation DialogText PromptProgress DialogCustom PopupTranslation ServiceBasic UsageContext TranslationMulti-Context TranslationStorage ServiceBasic UsageReactive ValuesNavigator ServiceBasic NavigationGame State NavigationFrom LuaUI Apps ServiceControls ServiceInfo Bar ServiceTop Bar ServiceScreen Cover ServiceContent ServicesCommon PatternsService in ComponentPersistent SettingsEvent-Driven UpdatesService Imports SummarySee Also