OverflowOverflow
UI Library

List menu

A keyboard-driven menu pinned to a screen corner. The player moves with the arrow keys, side-scrolls values, ticks checkboxes and confirms, without ever needing the mouse.

A list menu titled "Vehicle settings" with a highlighted row, two side-scroll rows, a checkbox and a fuel bar

registerListMenu

exports.of_ui:registerListMenu(data, cb)
FieldTypeDescription
idstringUnique id. Required.
titlestringHeader text. Required.
optionstable[]The rows. Required.
positionstring?top-left, top-right, bottom-left, bottom-right.
disableInputboolean?Default true: firing, melee and the weapon wheel stay suppressed while the menu is open. false leaves every control alone.
hardBlockboolean?true takes game input away entirely: the player stands still until the menu closes.
canCloseboolean?false removes ESC / Backspace.
onSelectedfunction?(selected, scrollIndex?, args?): fires as the highlight moves.
onSideScrollfunction?(selected, scrollIndex, args?): fires when a value row is scrolled.
onCheckfunction?(selected, checked, args?): fires when a checkbox is toggled.
onClosefunction?(keyPressed?): 'Escape' or 'Backspace'.

cb is the confirm handler: (selected, scrollIndex?, args?).

Every index handed to these callbacks is 1-based, matching the order you declared the options.

showListMenu

exports.of_ui:showListMenu(id, startIndex)
ArgumentTypeDescription
idstringA registered menu.
startIndexnumber?1-based row to start on. Defaults to the first.

hideListMenu

exports.of_ui:hideListMenu(onExit)

Closes the menu. Pass true to also fire its onClose.

setListMenuOptions

exports.of_ui:setListMenuOptions(id, options, index)

Replaces one option when index is given, or all of them when it is not. If that menu is currently open it refreshes in place, so you can rebuild rows while the player is looking at them.

getOpenListMenu

local id = exports.of_ui:getOpenListMenu()

Returns the id of the open menu, or nil.

Options

FieldTypeDescription
labelstringRow label.
descriptionstring?Muted second line.
iconstring?Icon on the left.
iconColorstring?Hex colour for the icon.
progressnumber?0–100 bar under the label.
colorSchemestring?Accent for the progress bar.
valuestable?Makes the row side-scrollable. Entries are strings or { label, description? }.
defaultIndexnumber?1-based starting position of a values row.
checkedboolean?Makes the row a checkbox with this initial state.
argstable?Passed to the callbacks. Never sent to the UI.
closeboolean?false keeps the menu open after confirming this row.

Example

exports.of_ui:registerListMenu({
    id = 'vehicle_settings',
    title = 'Vehicle settings',
    options = {
        { label = 'Engine', icon = 'car', description = 'Toggle the engine', close = false },
        { label = 'Seat', icon = 'chair', values = {
            'Driver', 'Passenger',
            { label = 'Rear left', description = 'Behind the driver' },
            'Rear right',
        } },
        { label = 'Radio', icon = 'radio', defaultIndex = 2, values = {
            'Off', 'Los Santos Rock', 'Non-Stop-Pop',
        } },
        { label = 'Doors locked', icon = 'lock', checked = true },
        { label = 'Fuel', icon = 'gas-pump', progress = 64, colorScheme = '#fdbe22' },
    },
    onSideScroll = function(selected, scrollIndex)
        if selected == 3 then TriggerEvent('radio:setStation', scrollIndex) end
    end,
    onCheck = function(selected, checked)
        if selected == 4 then TriggerEvent('vehicle:setLocked', checked) end
    end,
}, function(selected, scrollIndex)
    print(('confirmed row %d (value %s)'):format(selected, tostring(scrollIndex)))
end)

exports.of_ui:showListMenu('vehicle_settings')

Notes

  • Checkbox state is remembered. Toggling a checkbox updates the registry, so reopening the menu shows the state the player left it in.
  • close = false keeps the menu open after confirming: for rows the player toggles repeatedly.
  • The player can keep moving while the menu is open. Its keys (arrows, Enter, Backspace, ESC) are read on the Lua side and forwarded to the UI, so navigating the menu never costs the player their controls. disableInput (default true) still suppresses firing, melee and the weapon wheel; hardBlock = true is the opt-in for a menu that should freeze the player outright.

On this page