Menu
The Menu system provides hierarchical navigation menus with support for dynamic content, search functionality, and customizable layouts. Perfect for creating complex user interfaces with multiple levels of navigation.

registerMenu
Register a menu definition that can be displayed later with customizable structure and behavior.
-- Client-side
B2Lib.UI.registerMenu(menuId, menuData)
-- Server-side (via exports)
exports.b2lib:registerMenu(menuId, menuData)
Parameters:
menuId
(string): Unique menu identifier for registration and displaymenuData
(table): Menu configuration objecttitle
(string, optional): Menu title displayed in header (default: empty string)subtitle
(string, optional): Menu subtitle displayed below title (default: empty string)type
(string, optional): Menu layout type - 'list', 'grid', 'compact' (default: 'list')position
(string, optional): Menu position on screen (default: from config)showSearch
(boolean, optional): Enable search functionality (default: false)showHeader
(boolean, optional): Show menu header with title/subtitle (default: true)showClose
(boolean, optional): Show close button in header (default: true)maxHeight
(number, optional): Maximum menu height in pixels (default: 600)width
(number, optional): Menu width in pixels (default: from config)items
(table, optional): Array of menu items (default: empty array)searchPlaceholder
(string, optional): Search input placeholder textenableKeyboard
(boolean, optional): Enable keyboard navigation (default: true)closeOnSelect
(boolean, optional): Close menu when item selected (default: true)
Returns: boolean - Success status
Example:
-- Basic menu registration
local success = B2Lib.UI.registerMenu('main_menu', {
title = 'Main Menu',
subtitle = 'Select an option',
items = {
{ id = 'inventory', label = 'Inventory', icon = 'package', description = 'View your items' },
{ id = 'settings', label = 'Settings', icon = 'settings', description = 'Configure preferences' },
{ id = 'help', label = 'Help', icon = 'help-circle', description = 'Get assistance' }
}
})
-- Advanced menu with search and submenus
B2Lib.UI.registerMenu('player_management', {
title = 'Player Management',
subtitle = 'Administrative tools',
type = 'list',
position = 'center-left',
showSearch = true,
searchPlaceholder = 'Search players...',
maxHeight = 500,
width = 450,
items = {
{
id = 'kick_player',
label = 'Kick Player',
icon = 'user-x',
description = 'Remove player from server',
type = 'submenu',
submenu = 'kick_submenu'
},
{
id = 'ban_player',
label = 'Ban Player',
icon = 'ban',
description = 'Permanently ban player',
type = 'submenu',
submenu = 'ban_submenu'
},
{
id = 'teleport',
label = 'Teleport to Player',
icon = 'map-pin',
description = 'Teleport to selected player'
}
}
})
-- Grid layout menu
B2Lib.UI.registerMenu('vehicle_spawner', {
title = 'Vehicle Spawner',
type = 'grid',
position = 'center',
showSearch = true,
items = {
{ id = 'adder', label = 'Adder', icon = 'car', category = 'sports' },
{ id = 'zentorno', label = 'Zentorno', icon = 'car', category = 'sports' },
{ id = 'insurgent', label = 'Insurgent', icon = 'truck', category = 'military' }
}
})
showMenu
Display a registered menu with optional display options and context data.
-- Client-side
B2Lib.UI.showMenu(menuId, options)
-- Server-side (via exports)
exports.b2lib:showMenu(playerId, menuId, options)
Parameters:
playerId
(number, server-side only): Player server ID to show menu tomenuId
(string): Menu ID to display (must be registered first)options
(table, optional): Additional display optionsposition
(string, optional): Override default menu positioncontextData
(table, optional): Dynamic data to pass to menu itemsselectedIndex
(number, optional): Initially selected item indexsearchTerm
(string, optional): Pre-populate search fieldisSubMenu
(boolean, optional): Internal flag for submenu navigation
Returns: boolean - Success status
Example:
-- Basic menu display
local success = B2Lib.UI.showMenu('main_menu')
-- Menu with custom position and context
B2Lib.UI.showMenu('player_management', {
position = 'center-right',
contextData = {
currentPlayer = GetPlayerServerId(PlayerId()),
hasAdminPerms = true
}
})
-- Menu with pre-selected item and search
B2Lib.UI.showMenu('vehicle_spawner', {
selectedIndex = 2,
searchTerm = 'sports',
contextData = {
playerMoney = 50000,
allowedCategories = {'sports', 'sedans'}
}
})
-- Server-side menu display
exports.b2lib:showMenu(playerId, 'admin_panel', {
contextData = {
targetPlayer = targetPlayerId,
adminLevel = GetPlayerAdminLevel(playerId)
}
})
hideMenu
Hide the currently active menu with optional animation and cleanup.
-- Client-side
B2Lib.UI.hideMenu()
-- Server-side (via exports)
exports.b2lib:hideMenu(playerId)
Parameters:
playerId
(number, server-side only): Player server ID to hide menu for
Returns: boolean - Success status
Example:
-- Hide current menu
local success = B2Lib.UI.hideMenu()
-- Server-side menu hiding
exports.b2lib:hideMenu(playerId)
navigateToSubmenu
Navigate to a registered submenu while maintaining navigation history.
-- Client-side
B2Lib.UI.navigateToSubmenu(submenuId)
-- Server-side (via exports)
exports.b2lib:navigateToSubmenu(playerId, submenuId)
Parameters:
playerId
(number, server-side only): Player server IDsubmenuId
(string): Submenu ID to navigate to (must be registered)
Returns: boolean - Success status
Example:
-- Navigate to submenu
local success = B2Lib.UI.navigateToSubmenu('settings_submenu')
-- Server-side submenu navigation
exports.b2lib:navigateToSubmenu(playerId, 'admin_tools_submenu')
updateMenuItems
Update menu items dynamically for an active or registered menu.
-- Client-side
B2Lib.UI.updateMenuItems(menuId, items)
-- Server-side (via exports)
exports.b2lib:updateMenuItems(playerId, menuId, items)
Parameters:
playerId
(number, server-side only): Player server IDmenuId
(string): Menu ID to update items foritems
(table): New items array to replace existing items
Returns: boolean - Success status
Example:
-- Update menu items with new data
local newItems = {
{ id = 'new_option', label = 'New Option', icon = 'plus' },
{ id = 'updated_option', label = 'Updated Option', icon = 'edit' }
}
local success = B2Lib.UI.updateMenuItems('main_menu', newItems)
-- Server-side item updates
local playerItems = GetPlayerInventoryItems(playerId)
exports.b2lib:updateMenuItems(playerId, 'inventory_menu', playerItems)
getActiveMenu
Get information about the currently active menu.
B2Lib.UI.getActiveMenu()
Returns: table|nil - Active menu data or nil if no menu is active
id
(string): Menu identifiertitle
(string): Menu titletype
(string): Menu typeposition
(string): Menu positionitemCount
(number): Number of itemsselectedIndex
(number): Currently selected item indexsearchTerm
(string): Current search term
Example:
local activeMenu = B2Lib.UI.getActiveMenu()
if activeMenu then
print('Active menu:', activeMenu.id)
print('Selected item:', activeMenu.selectedIndex)
end
isMenuActive
Check if a specific menu is currently active.
B2Lib.UI.isMenuActive(menuId)
Parameters:
menuId
(string, optional): Menu ID to check (if nil, checks if any menu is active)
Returns: boolean - True if specified menu (or any menu) is active
Example:
-- Check if any menu is active
local anyMenuActive = B2Lib.UI.isMenuActive()
-- Check if specific menu is active
local mainMenuActive = B2Lib.UI.isMenuActive('main_menu')
Menu Item Structure
Menu items support various properties for different functionality and appearance:
local menuItem = {
id = 'unique_item_id', -- Unique item identifier (required)
label = 'Item Label', -- Display text (required)
description = 'Item description', -- Optional description/tooltip
icon = 'icon_name', -- Optional icon name (Lucide icons)
type = 'action', -- Item type: 'action', 'submenu', 'separator', 'header'
value = 'item_value', -- Optional value/data associated with item
disabled = false, -- Whether item is disabled
hidden = false, -- Whether item is hidden
color = 'default', -- Item color: 'default', 'primary', 'success', 'warning', 'error'
badge = '5', -- Optional badge text/number
shortcut = 'Ctrl+S', -- Optional keyboard shortcut display
submenu = 'submenu_id', -- Submenu ID for submenu items
category = 'general', -- Optional category for filtering
metadata = {}, -- Custom metadata object
onClick = function(item, menu) -- Optional click handler function
-- Handle item selection
end
}
Menu Types
List Menu
Standard vertical list layout with icons and descriptions.
B2Lib.UI.registerMenu('list_menu', {
title = 'List Menu Example',
type = 'list',
items = {
{ id = 'item1', label = 'First Item', icon = 'file', description = 'First item description' },
{ id = 'item2', label = 'Second Item', icon = 'folder', description = 'Second item description' }
}
})
Grid Menu
Grid layout for visual items like vehicles, weapons, or categories.
B2Lib.UI.registerMenu('grid_menu', {
title = 'Grid Menu Example',
type = 'grid',
items = {
{ id = 'car1', label = 'Sports Car', icon = 'car' },
{ id = 'car2', label = 'SUV', icon = 'truck' },
{ id = 'car3', label = 'Motorcycle', icon = 'bike' }
}
})
Compact Menu
Minimal layout for simple option lists.
B2Lib.UI.registerMenu('compact_menu', {
title = 'Compact Menu',
type = 'compact',
items = {
{ id = 'yes', label = 'Yes' },
{ id = 'no', label = 'No' },
{ id = 'cancel', label = 'Cancel' }
}
})
Server-Side Usage
Server-side menu functions allow you to manage menus for specific players:
-- Register menu on server (available to all players)
exports.b2lib:registerMenu('server_menu', {
title = 'Server Menu',
items = {
{ id = 'announce', label = 'Make Announcement', icon = 'megaphone' },
{ id = 'restart', label = 'Restart Server', icon = 'refresh-cw' }
}
})
-- Show menu to specific player
exports.b2lib:showMenu(playerId, 'server_menu')
-- Update menu for specific player
local adminItems = GetAdminMenuItems(playerId)
exports.b2lib:updateMenuItems(playerId, 'admin_menu', adminItems)
-- Hide menu for specific player
exports.b2lib:hideMenu(playerId)
Configuration
Complete menu configuration options in config.lua
:
Config.Menu = {
position = 'center-left', -- Default position for menus
width = 400, -- Default menu width in pixels
enableSearch = true, -- Enable search functionality
closeOnClick = true, -- Close menu when clicking outside
maxItems = 10 -- Maximum items per menu page
}
Config.ComponentStyles.menu = {
width = '400px', -- Menu width
maxHeight = '500px', -- Maximum menu height
padding = '16px', -- Internal padding
borderRadius = 'xl', -- Large corner rounding
shadow = 'xl', -- Large drop shadow
borderWidth = '1px', -- Border thickness
header = {
padding = '12px 16px', -- Header padding
titleFontSize = 'lg', -- Title text size
titleFontWeight = 'semibold' -- Title font weight
},
item = {
padding = '12px 16px', -- Item padding
borderRadius = 'md', -- Item corner rounding
fontSize = 'sm', -- Item text size
iconSize = '16px', -- Item icon size
spacing = '12px' -- Spacing between items
}
}
Advanced Examples
Dynamic Player List Menu
local function createPlayerListMenu()
local players = GetActivePlayers()
local playerItems = {}
for _, playerId in ipairs(players) do
local playerName = GetPlayerName(playerId)
local playerPing = GetPlayerPing(playerId)
table.insert(playerItems, {
id = 'player_' .. playerId,
label = playerName,
description = 'Ping: ' .. playerPing .. 'ms',
icon = 'user',
value = playerId,
badge = tostring(playerId)
})
end
B2Lib.UI.registerMenu('player_list', {
title = 'Online Players',
subtitle = #playerItems .. ' players online',
showSearch = true,
searchPlaceholder = 'Search players...',
items = playerItems
})
B2Lib.UI.showMenu('player_list')
end
-- Update player list every 5 seconds
CreateThread(function()
while true do
if B2Lib.UI.isMenuActive('player_list') then
createPlayerListMenu()
end
Wait(5000)
end
end)
Multi-Level Admin Menu
-- Main admin menu
B2Lib.UI.registerMenu('admin_main', {
title = 'Admin Panel',
subtitle = 'Administrative tools',
items = {
{
id = 'player_management',
label = 'Player Management',
icon = 'users',
description = 'Manage online players',
type = 'submenu',
submenu = 'admin_players'
},
{
id = 'server_management',
label = 'Server Management',
icon = 'server',
description = 'Server administration',
type = 'submenu',
submenu = 'admin_server'
},
{
id = 'vehicle_management',
label = 'Vehicle Management',
icon = 'car',
description = 'Spawn and manage vehicles',
type = 'submenu',
submenu = 'admin_vehicles'
}
}
})
-- Player management submenu
B2Lib.UI.registerMenu('admin_players', {
title = 'Player Management',
subtitle = 'Select a player action',
items = {
{ id = 'kick', label = 'Kick Player', icon = 'user-x', description = 'Remove player from server' },
{ id = 'ban', label = 'Ban Player', icon = 'ban', description = 'Permanently ban player' },
{ id = 'teleport', label = 'Teleport to Player', icon = 'map-pin', description = 'Teleport to player location' },
{ id = 'spectate', label = 'Spectate Player', icon = 'eye', description = 'Watch player activity' }
}
})
-- Server management submenu
B2Lib.UI.registerMenu('admin_server', {
title = 'Server Management',
subtitle = 'Server administration tools',
items = {
{ id = 'announce', label = 'Server Announcement', icon = 'megaphone', description = 'Send message to all players' },
{ id = 'restart', label = 'Restart Server', icon = 'refresh-cw', description = 'Restart the server', color = 'warning' },
{ id = 'weather', label = 'Change Weather', icon = 'cloud', description = 'Modify weather conditions' },
{ id = 'time', label = 'Change Time', icon = 'clock', description = 'Adjust server time' }
}
})
Inventory Menu with Categories
local function createInventoryMenu(playerItems)
local categories = {}
local menuItems = {}
-- Group items by category
for _, item in ipairs(playerItems) do
if not categories[item.category] then
categories[item.category] = {}
end
table.insert(categories[item.category], item)
end
-- Create category headers and items
for categoryName, items in pairs(categories) do
-- Add category header
table.insert(menuItems, {
id = 'header_' .. categoryName,
label = categoryName:upper(),
type = 'header',
icon = 'folder'
})
-- Add category items
for _, item in ipairs(items) do
table.insert(menuItems, {
id = item.name,
label = item.label,
description = 'Quantity: ' .. item.count,
icon = item.icon or 'package',
badge = tostring(item.count),
value = item,
category = categoryName
})
end
-- Add separator after category
table.insert(menuItems, {
id = 'sep_' .. categoryName,
type = 'separator'
})
end
B2Lib.UI.registerMenu('inventory', {
title = 'Inventory',
subtitle = 'Your items',
showSearch = true,
searchPlaceholder = 'Search items...',
maxHeight = 600,
items = menuItems
})
B2Lib.UI.showMenu('inventory')
end
Context-Aware Vehicle Menu
local function createVehicleMenu(playerData)
local vehicleItems = {}
-- Add owned vehicles
for _, vehicle in ipairs(playerData.ownedVehicles) do
table.insert(vehicleItems, {
id = 'owned_' .. vehicle.plate,
label = vehicle.name,
description = 'Plate: ' .. vehicle.plate,
icon = 'car',
badge = 'OWNED',
color = 'success',
value = vehicle
})
end
-- Add rental vehicles if player has rental permissions
if playerData.canRent then
table.insert(vehicleItems, {
id = 'separator_rental',
type = 'separator'
})
for _, vehicle in ipairs(GetRentalVehicles()) do
table.insert(vehicleItems, {
id = 'rental_' .. vehicle.model,
label = vehicle.name,
description = 'Rental: $' .. vehicle.price .. '/hour',
icon = 'key',
badge = 'RENTAL',
color = 'warning',
value = vehicle
})
end
end
B2Lib.UI.registerMenu('vehicle_menu', {
title = 'Vehicle Management',
subtitle = playerData.ownedVehicles and #playerData.ownedVehicles .. ' owned vehicles' or 'No vehicles',
showSearch = true,
items = vehicleItems
})
B2Lib.UI.showMenu('vehicle_menu', {
contextData = playerData
})
end
Search-Enabled Command Menu
local function createCommandMenu()
local commands = {
{ name = '/tp', description = 'Teleport to coordinates', category = 'teleport' },
{ name = '/tpplayer', description = 'Teleport to player', category = 'teleport' },
{ name = '/heal', description = 'Heal yourself', category = 'health' },
{ name = '/armor', description = 'Give armor', category = 'health' },
{ name = '/car', description = 'Spawn vehicle', category = 'vehicles' },
{ name = '/dv', description = 'Delete vehicle', category = 'vehicles' }
}
local menuItems = {}
for _, cmd in ipairs(commands) do
table.insert(menuItems, {
id = cmd.name,
label = cmd.name,
description = cmd.description,
icon = 'terminal',
category = cmd.category,
value = cmd.name
})
end
B2Lib.UI.registerMenu('command_menu', {
title = 'Command Reference',
subtitle = 'Available commands',
showSearch = true,
searchPlaceholder = 'Search commands...',
items = menuItems
})
B2Lib.UI.showMenu('command_menu')
end
Events
The Menu system triggers comprehensive events for handling user interactions:
b2lib:menu-item-selected
Triggered when user selects a menu item.
AddEventHandler('b2lib:menu-item-selected', function(data)
print('Menu item selected:', data.item.id)
print('Menu ID:', data.menuId)
print('Item data:', json.encode(data.item))
-- Handle different item types
if data.item.type == 'submenu' then
print('Navigating to submenu:', data.item.submenu)
else
print('Action item selected:', data.item.value)
end
end)
b2lib:menu-closed
Triggered when a menu is closed.
AddEventHandler('b2lib:menu-closed', function(menuId)
print('Menu closed:', menuId)
-- Cleanup or save state
if menuId == 'settings_menu' then
SavePlayerSettings()
end
end)
b2lib:menu-search
Triggered when user searches in a menu.
AddEventHandler('b2lib:menu-search', function(data)
print('Menu search:', data.searchTerm)
print('Results count:', data.resultCount)
-- Update menu based on search
if data.menuId == 'player_list' then
UpdatePlayerListSearch(data.searchTerm)
end
end)
b2lib:submenu-navigated
Triggered when navigating to a submenu.
AddEventHandler('b2lib:submenu-navigated', function(data)
print('Navigated to submenu:', data.submenuId)
print('From menu:', data.parentMenuId)
-- Load submenu data
if data.submenuId == 'admin_players' then
LoadPlayerManagementData()
end
end)
Best Practices
Use Descriptive Menu IDs: Choose clear, unique identifiers that describe the menu's purpose
Implement Search for Large Lists: Enable search functionality for menus with more than 10 items
Provide Clear Item Descriptions: Include helpful descriptions for complex or ambiguous menu items
Use Appropriate Icons: Select relevant Lucide icons that enhance visual understanding
Group Related Items: Use separators and headers to organize menu items logically
Handle Menu Events: Always implement event handlers for menu interactions
Update Dynamic Content: Refresh menu items when underlying data changes
Consider Performance: Avoid creating menus with hundreds of items; use pagination instead
Test Keyboard Navigation: Ensure all menu functions work with keyboard-only input
Provide Context Data: Pass relevant context information to menu items for dynamic behavior
Troubleshooting
Menu Not Displaying
Problem: Menu doesn't appear when calling showMenu()
Solutions:
-- Check if menu is registered
if not B2Lib.UI.isMenuRegistered('my_menu') then
print('Menu not registered!')
B2Lib.UI.registerMenu('my_menu', menuData)
end
-- Verify menu ID spelling
B2Lib.UI.showMenu('my_menu') -- Correct
-- B2Lib.UI.showMenu('my-menu') -- Wrong if registered with underscore
-- Check for conflicting UI elements
B2Lib.UI.hideMenu() -- Close any existing menu first
B2Lib.UI.showMenu('my_menu')
Menu Items Not Updating
Problem: updateMenuItems()
doesn't refresh the display
Solutions:
-- Ensure menu is active before updating
if B2Lib.UI.isMenuActive('my_menu') then
B2Lib.UI.updateMenuItems('my_menu', newItems)
else
print('Menu not active, cannot update items')
end
-- Force menu refresh
B2Lib.UI.hideMenu()
B2Lib.UI.registerMenu('my_menu', { items = newItems })
B2Lib.UI.showMenu('my_menu')
Submenu Navigation Issues
Problem: Submenu navigation not working properly
Solutions:
-- Ensure submenu is registered before navigation
B2Lib.UI.registerMenu('main_menu', mainMenuData)
B2Lib.UI.registerMenu('sub_menu', subMenuData) -- Register submenu separately
-- Check submenu ID in item definition
local menuItem = {
id = 'settings',
label = 'Settings',
type = 'submenu',
submenu = 'sub_menu' -- Must match registered submenu ID
}
-- Handle navigation events
AddEventHandler('b2lib:menu-item-selected', function(data)
if data.item.type == 'submenu' then
if not B2Lib.UI.isMenuRegistered(data.item.submenu) then
print('Submenu not registered:', data.item.submenu)
end
end
end)
Search Functionality Not Working
Problem: Menu search doesn't filter items correctly
Solutions:
-- Enable search in menu registration
B2Lib.UI.registerMenu('searchable_menu', {
title = 'Searchable Menu',
showSearch = true, -- Must be enabled
searchPlaceholder = 'Search items...',
items = menuItems
})
-- Ensure items have searchable properties
local searchableItem = {
id = 'item1',
label = 'Searchable Label', -- Searched by default
description = 'Searchable description', -- Also searched
category = 'searchable_category', -- Include category for better search
keywords = 'additional search terms' -- Custom search keywords
}
Performance Issues with Large Menus
Problem: Menu becomes slow with many items
Solutions:
-- Implement pagination for large datasets
local function createPaginatedMenu(allItems, page, itemsPerPage)
local startIndex = (page - 1) * itemsPerPage + 1
local endIndex = math.min(startIndex + itemsPerPage - 1, #allItems)
local pageItems = {}
for i = startIndex, endIndex do
table.insert(pageItems, allItems[i])
end
-- Add navigation items
if page > 1 then
table.insert(pageItems, 1, {
id = 'prev_page',
label = '← Previous Page',
icon = 'chevron-left'
})
end
if endIndex < #allItems then
table.insert(pageItems, {
id = 'next_page',
label = 'Next Page →',
icon = 'chevron-right'
})
end
return pageItems
end
-- Use virtual scrolling for very large lists
B2Lib.UI.registerMenu('large_menu', {
title = 'Large Dataset',
maxHeight = 400, -- Limit height to enable scrolling
items = createPaginatedMenu(largeDataset, 1, 20)
})
Menu Positioning Issues
Problem: Menu appears in wrong position or off-screen
Solutions:
-- Use valid position values
local validPositions = {
'top-left', 'top-center', 'top-right',
'center-left', 'center', 'center-right',
'bottom-left', 'bottom-center', 'bottom-right'
}
-- Override position when showing menu
B2Lib.UI.showMenu('my_menu', {
position = 'center' -- Override default position
})
-- Check screen resolution compatibility
B2Lib.UI.registerMenu('responsive_menu', {
title = 'Responsive Menu',
position = 'center', -- Safe default position
width = math.min(400, GetScreenWidth() * 0.8) -- Responsive width
})
Last updated