From the reference libraryRedM

VORP Menu System Documentation

vorp/vorp_menu_docs.md

VORP Menu System Documentation

VORP Menu is a library that allows you to create menus in-game with an RDR2 style interface. This documentation covers the complete menu system for developers.

Table of Contents

  1. Getting Started
  2. Menu Elements
  3. Menu Configuration
  4. Menu Functions
  5. Runtime Menu Management
  6. Complete Menu Example

Getting Started > Get Menu Data

Add this to the top of your client scripts to get the menu data setters and getters:

local Menu = exports.vorp_menu:GetMenuData()

Returns: The menu data object containing all menu functions and properties.

Getting Started > Close All Menus

Close all currently open menus:

MenuData.CloseAll()

Menu Elements

Menu elements are the components that will be displayed in the menu. Different element types provide various interaction methods.

Menu Elements > Basic Element Structure

local MenuElements = {
  {
    label = "name",           -- Display name
    value = "value",          -- Element value
    desc = "description"      -- Element description
  }
}

Menu Elements > Slider Element

Create slider elements for numeric input with min/max values:

local MenuElements = {
  {
    label = "name",
    value = 0,                -- Current slider value
    desc = "description",
    type = "slider",          -- Element type
    min = 0,                  -- Minimum value (can be float)
    max = 10,                 -- Maximum value (can be float)
    hop = 1                   -- Step/increment value
  }
}

Properties:

  • type - Must be "slider" for slider elements
  • min - Minimum value (supports floats)
  • max - Maximum value (supports floats)
  • hop - Step value for increments

Menu Elements > Advanced Element with Custom Properties

local MenuElements = {
  {
    label = "name",
    value = "value",
    desc = "description",
    itemHeight = "4vh",       -- Custom height for this element
    any = any,                -- Custom property (any value type)
    table = {                 -- Nested table data
      string = "string",
      number = 111,
      table = table,
    }
  }
}

Custom Properties:

  • itemHeight - Override default element height
  • any - Any custom value (string, number, table, array, etc.)
  • table - Nested table within element for complex data

Menu Configuration > Menu Information Object

local MenuInfo = {
  title = "menu title",               -- Menu title
  subtext = "menu sub text",          -- Menu subtitle
  align = "top-right",                -- Menu alignment
  elements = MenuElements,            -- Menu elements array
  lastmenu = "function",              -- Previous menu function name (optional)
  itemHeight = "4vh"                  -- Default height for all elements (optional)
}

Configuration Properties:

  • title - The title displayed at the top of the menu
  • subtext - Subtitle text below the title
  • align - Menu position: "top-right", "top-center", "top-left"
  • elements - Array of menu elements
  • lastmenu - Function name for returning to previous menu (optional)
  • itemHeight - Default height applied to all elements if not defined individually (optional)

Menu Functions > Opening a Menu

Menu.Open("default", GetCurrentResourceName(), "OpenMenu", MenuInfo, 
  function(data, menu)
    -- Selection callback
  end,
  function(data, menu)
    -- Close/back callback
  end
)

Parameters:

  • "default" - Menu type
  • GetCurrentResourceName() - Resource name
  • "OpenMenu" - Unique namespace (allows menu to remember position)
  • MenuInfo - Menu configuration object
  • function(data, menu) - Selection callback function
  • function(data, menu) - Close/back callback function

Menu Functions > Selection Callback Data

function(data, menu)
  print("current key of MenuElements is " .. data.current.index)
  print("current value of MenuElements is " .. data.current.value)
  
  -- Access custom element properties
  if data.current.any then
    print("Custom property:", data.current.any)
  end
  
  -- Handle back button
  if (data.current == "backup") then
    return _G[data.trigger](any, any) -- Call previous menu function
  end
  
  -- Handle specific values
  if data.current.value == "value" then
    return
  end
  
  if data.current.info == "param" then
    return menu.close()
  end
end

Runtime Menu Management > Add New Element

Add elements to the current menu during runtime:

menu.addNewElement({
  label = "label",
  value = "open", 
  desc = "description"
})

-- Refresh menu to show new element
menu.refresh()

Parameters:

  • label - The display label of the element
  • value - The value of the element
  • desc - The description of the element

Runtime Menu Management > Find Elements

Retrieve elements by value or index:

local element = menu.getElementByValue(value)
local element = menu.getElementByIndex(index)

Parameters:

  • value - Element value to search for
  • index - Element index to retrieve

Returns: Element object if found

Runtime Menu Management > Update Element

Update specific element properties:

menu.setElement(index, variable, newValue)

-- Refresh menu to show changes
menu.refresh()

Parameters:

  • index - The index of the menu element to update
  • variable - The property to update (label, value, desc, or any custom property)
  • newValue - The new value for the property

Runtime Menu Management > Remove Elements

Remove elements by index or value:

menu.removeElementByIndex(index, loop)
menu.removeElementByValue(value, loop)

-- Refresh menu to show changes
menu.refresh()

Parameters:

  • index - Element index to remove
  • value - Element value to remove
  • loop - Stop at first match (false) or continue searching (true)

Runtime Menu Management > Menu Control Functions

menu.close()              -- Close current menu
menu.setTitle("title")    -- Set menu title
menu.refresh()            -- Refresh menu display

Complete Menu Example

function OpenMenu()
  Menu.CloseAll()
  
  local MenuElements = {
    {
      label = "Basic Item",
      value = "basic",
      desc = "A basic menu item"
    },
    {
      label = "Volume Slider",
      value = 50,
      desc = "Adjust volume level",
      type = "slider",
      min = 0,
      max = 100,
      hop = 5
    },
    {
      label = "Custom Height Item",
      value = "custom",
      desc = "Item with custom height",
      itemHeight = "6vh"
    },
    {
      label = "Advanced Item",
      value = "advanced",
      desc = "Item with custom data",
      customData = "example",
      settings = {
        enabled = true,
        priority = 1,
        options = {"option1", "option2"}
      }
    }
  }

  Menu.Open("default", GetCurrentResourceName(), "OpenMenu",
    {
      title = "Example Menu",
      subtext = "Choose an option",
      align = "top-right",
      elements = MenuElements,
      lastmenu = nil,
      itemHeight = "4vh"
    },
    function(data, menu)
      print("Selected index: " .. data.current.index)
      print("Selected value: " .. data.current.value)
      
      -- Handle back button
      if (data.current == "backup") then
        return menu.close()
      end
      
      -- Handle different menu items
      if data.current.value == "basic" then
        print("Basic item selected")
        -- Add new element dynamically
        menu.addNewElement({
          label = "New Item",
          value = "new",
          desc = "Dynamically added item"
        })
        menu.refresh()
        
      elseif data.current.value == "custom" then
        print("Custom item selected")
        -- Update menu title
        menu.setTitle("Updated Menu")
        menu.refresh()
        
      elseif data.current.value == "advanced" then
        print("Advanced item selected")
        print("Custom data: " .. data.current.customData)
        print("Settings: " .. json.encode(data.current.settings))
        
      elseif data.current.value == "new" then
        print("New item selected")
        -- Remove this element
        menu.removeElementByValue("new")
        menu.refresh()
        
      elseif data.current.type == "slider" then
        print("Slider value: " .. data.current.value)
        -- Handle slider value change
      end
    end,
    function(data, menu)
      -- Handle menu close/back
      print("Menu closed or back pressed")
      menu.close()
    end
  )
end

-- Example usage
RegisterCommand("openmenu", function()
  OpenMenu()
end)

Complete Menu Example > Dynamic Menu Management Example

function CreateDynamicMenu()
  Menu.CloseAll()
  
  local MenuElements = {
    {
      label = "Add Item",
      value = "add",
      desc = "Add a new menu item"
    },
    {
      label = "Remove Item",
      value = "remove", 
      desc = "Remove the last item"
    }
  }

  Menu.Open("default", GetCurrentResourceName(), "DynamicMenu",
    {
      title = "Dynamic Menu",
      subtext = "Manage menu items",
      align = "top-center",
      elements = MenuElements
    },
    function(data, menu)
      if data.current.value == "add" then
        local itemCount = #menu.getElements() + 1
        menu.addNewElement({
          label = "Dynamic Item " .. itemCount,
          value = "dynamic_" .. itemCount,
          desc = "Dynamically created item"
        })
        menu.refresh()
        
      elseif data.current.value == "remove" then
        local elements = menu.getElements()
        if #elements > 2 then -- Keep original add/remove buttons
          menu.removeElementByIndex(#elements)
          menu.refresh()
        end
        
      elseif string.match(data.current.value, "dynamic_") then
        print("Selected dynamic item: " .. data.current.label)
      end
    end,
    function(data, menu)
      menu.close()
    end
  )
end

Best Practices > Menu Organization

  1. Use Descriptive Labels: Make menu items clear and understandable
  2. Logical Grouping: Group related items together
  3. Consistent Naming: Use consistent value naming conventions

Best Practices > Performance Considerations

  1. Refresh Sparingly: Only call menu.refresh() when necessary
  2. Clean Up: Always close menus when done
  3. Memory Management: Remove unused elements to free memory

Best Practices > User Experience

  1. Provide Feedback: Use descriptions to explain what items do
  2. Logical Flow: Design menu navigation that makes sense
  3. Error Handling: Always check if elements exist before operations

Best Practices > Example Error Handling

-- Safe element retrieval
local element = menu.getElementByValue("somevalue")
if element then
  -- Process element
else
  print("Element not found")
end

-- Safe element update
if menu.getElementByIndex(index) then
  menu.setElement(index, "label", "New Label")
  menu.refresh()
end

Notes

  • Menus use RDR2-style interface design
  • All menu operations are client-side
  • Refresh menu after runtime modifications
  • Use unique namespaces to prevent conflicts
  • Element properties can be extended with custom data
  • Slider elements support float values for precision
  • Menu alignment affects where the menu appears on screen
  • Always handle both selection and close callbacks for complete functionality
Back to documentation