A production-ready, modular UI library for FiveM featuring a React-based NUI frontend and a clean Lua API. Build beautiful menus, notifications, and dialogs with minimal code.
- Features
- Tech Stack
- Installation
- Usage Methods
- Usage
- Keyboard and Mouse Navigation
- Full API Reference
- Complete Example
- Development
- Project Structure
- License
- Fully navigable menus with keyboard and mouse wheel
- Description option on all items (button, checkbox, list, slider, submenu, category, search, info, separator): when set, a panel below the menu shows that text for the focused item
- Button, checkbox, list selector, slider, separator, submenu, category, and info items
- Categories: expand/collapse groups of items in-place (e.g. "Actions on player" → Freeze, Kick, Ban); items can be assigned to a category and only show when it is expanded
- Info button: hover or focus to show a side panel with label/value pairs (e.g. player name, group, job)
- Search/filter functionality for large menus
- Submenu stack navigation with back support
- Custom accent colors and banner images (default accent: red)
- Icon support (FontAwesome names or image URLs)
- Optional category on any item so it appears only when that category is expanded
- Four notification types: success, error, warning, info
- Optional custom accent color per notification
- Configurable display duration
- Optional body message and icon
- Bulk clear support
- Black background with colored accent bar and icon badge
- Input dialogs with field types: text, number, password, textarea
- Optional accent color and header icon per dialog
- Confirm/cancel dialogs with one-liner shortcut (with optional color)
- Customizable buttons with primary, secondary, danger variants and optional icon
- Field validation (required, maxLength, min/max)
- Closable toggle
- Default accent color: red when not specified
- Entity targeting with ALT key: hold ALT to show cursor, left-click an entity to open context menu
- Entity highlighting: targeted entities become semi-transparent for visual feedback
- Camera lock: camera is locked while ALT is held, movement (WASD) remains active
- Release ALT to close: releasing ALT closes everything instantly
- Flyout submenus: submenus expand to the right on hover (nested submenus supported)
- Shine hover effect: items glow with red accent border on hover, consistent with the menu system
- Register options by entity type:
vehicle,ped,player,object,myself,mycar, orall - Target yourself:
myselftype for options on your own character - Target your vehicle:
mycartype for options on the vehicle you are sitting in (inheritsvehicleoptions) - Target a specific entity:
entityfield to bind options to a single entity handle - Target by model/prop:
modelfield to bind options to all props of a given model name or hash - Auto-cleanup: options are automatically removed when the resource that registered them stops
- Interaction prompt at world coordinates (above peds, doors, etc.)
- Optional key (e.g.
E) displayed in a small box on the left; omitkeyto show only the label - Key press animation on the key box when the player presses the key
- Dynamic coords: pass a function for moving entities (e.g. ped head)
- Distance-based visibility and
onSelectcallback
- Same as Interact but hold the key for a
duration(ms) - Progress bar below the label while holding;
onSelectonly when full duration complete - onCancel when the player releases early or leaves range
- Blocking function: returns
true(completed) orfalse(cancelled) - Animation support: play animations during the progress (dict + clip)
- Prop attachment: attach props to the player (model, position, rotation, bone)
- Cancel support: optional ESC/Backspace cancellation
- Control disabling: selectively disable movement, vehicle, combat, or mouse controls
- Uses the global accent color with glow effect
- UI sound effects (hover, select, toggle) with global toggle
- Smooth animations and hover shine effects
- Plugin system for extensibility
- Hot reload dev playground (no FiveM required)
- Optimized production build
- Lua API split by component:
lua/api/(menu, notification, dialog, progressbar, context, config, exports)
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript (strict), Vite 6 |
| State | Zustand 5 |
| Styling | TailwindCSS 3 |
| Animation | Framer Motion 11 |
| Backend | FiveM Lua (client-side) |
cd zedlib/web
npm install
npm run buildCopy the zedlib folder into your server's resources/ directory, then add it to your server.cfg:
ensure zedlibOption A — Import file (recommended)
Add zedlib as a dependency and include import.lua in your fxmanifest.lua. This gives you the zed.* API with automatic cross-resource callback support:
fx_version 'cerulean'
game 'gta5'
dependencies { 'zedlib' }
shared_scripts {
'@zedlib/import.lua',
}
client_scripts {
'client.lua',
}You can then use the zed.* namespace in your scripts:
zed.CreateMenu("my_menu", "My Menu")Option B — Exports
Add zedlib as a dependency and call functions through FiveM exports. No extra file needed:
fx_version 'cerulean'
game 'gta5'
dependencies { 'zedlib' }
client_scripts {
'client.lua',
}You can then call any function via exports.zedlib:
exports.zedlib:CreateMenu("my_menu", "My Menu")Both methods provide the exact same functionality. The
zed.*import is generally more convenient for scripts with many callbacks.
ZedLib exposes every function through three equivalent interfaces. Pick whichever suits your project:
| Interface | Scope | Example |
|---|---|---|
zed.* |
Any resource that imports @zedlib/import.lua |
zed.OpenMenu("main") |
exports.zedlib:* |
Any resource with zedlib as dependency |
exports.zedlib:OpenMenu("main") |
UI.* |
Internal to the zedlib resource itself | UI.OpenMenu("main") |
All code examples below use the zed.* style. Replace with exports.zedlib: if you prefer exports.
zed.CreateMenu("main", "Main Menu", "Select an option", {
color = "#3498db", -- accent color (hex)
banner = "https://example.com/banner.png" -- header banner image URL
})| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Unique menu identifier |
title |
string | yes | Title displayed in the menu header |
subtitle |
string | no | Subtitle below the title (hidden when a banner is set) |
opts |
table | no | Additional options: color (hex string), banner (image URL). Default accent color when not set: red (#e74c3c). |
zed.AddButton("main", {
label = "Teleport to Marker",
description = "Teleports you to your map marker",
icon = "location-dot",
rightLabel = "$725,000",
rightLabelColor = "#22c55e",
onSelect = function()
local blip = GetFirstBlipInfoId(8)
if DoesBlipExist(blip) then
local coords = GetBlipInfoIdCoord(blip)
SetEntityCoords(PlayerPedId(), coords.x, coords.y, coords.z)
zed.Notify({ title = "Teleported", message = "You have been teleported to the marker" })
end
end
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Display text for the button |
description |
string | nil | Text shown in the description panel below the menu when this item is focused |
icon |
string | nil | FontAwesome icon name (e.g. "gear") or image URL |
rightLabel |
string | nil | Display text in the right button |
rightLabelColor |
string | nil | Color of the right label |
id |
string | auto | Custom unique identifier |
disabled |
boolean | false | Prevents interaction when true |
category |
string | nil | Category id: item is only visible when this category is expanded |
metadata |
table | nil | Arbitrary data passed to the onSelect callback |
infoData |
table | nil | Array of { label, value } pairs shown in the info side panel when focused |
onSelect |
function | nil | Callback fired when the button is selected |
zed.AddCheckbox("main", {
label = "God Mode",
icon = "shield",
checked = false,
onChange = function(checked)
print("God Mode:", checked)
SetEntityInvincible(PlayerPedId(), checked)
end
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Display text |
icon |
string | nil | FontAwesome icon name or image URL |
id |
string | auto | Custom unique identifier |
disabled |
boolean | false | Prevents interaction when true |
category |
string | nil | Category id: item only visible when this category is expanded |
checked |
boolean | false | Initial checked state |
infoData |
table | nil | Array of { label, value } pairs shown in the info side panel when focused |
onChange |
function | nil | Callback receiving the new checked boolean |
Use left/right arrow keys to cycle through options.
zed.AddList("main", {
label = "Weather",
icon = "cloud-sun",
items = {
{ label = "Clear", value = "CLEAR" },
{ label = "Rain", value = "RAIN" },
{ label = "Thunder", value = "THUNDER" },
{ label = "Snow", value = "SNOW" },
},
currentIndex = 1,
onChange = function(index, item)
print("Selected:", item.label, "Value:", item.value, "Index:", index)
SetWeatherTypeNow(item.value)
end
})You can also pass plain strings — they are auto-converted to { label = str, value = str }:
zed.AddList("main", {
label = "Color",
items = { "Red", "Green", "Blue" },
onChange = function(index, item)
print(item.value) -- "Red", "Green", or "Blue"
end
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Display text |
icon |
string | nil | FontAwesome icon name or image URL |
id |
string | auto | Custom unique identifier |
disabled |
boolean | false | Prevents interaction when true |
category |
string | nil | Category id: item only visible when this category is expanded |
items |
table | — | Array of { label, value } tables or plain strings |
currentIndex |
number | 1 | Initial selected index (1-based) |
infoData |
table | nil | Array of { label, value } pairs shown in the info side panel when focused |
onChange |
function | nil | Callback receiving (index, item) — index is 1-based |
Use left/right arrow keys to adjust the value.
zed.AddSlider("main", {
label = "Vehicle Speed",
icon = "gauge-high",
min = 0,
max = 200,
step = 10,
value = 80,
onChange = function(value)
print("Speed set to:", value)
SetVehicleMaxSpeed(GetVehiclePedIsIn(PlayerPedId(), false), value + 0.0)
end
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Display text |
icon |
string | nil | FontAwesome icon name or image URL |
id |
string | auto | Custom unique identifier |
disabled |
boolean | false | Prevents interaction when true |
category |
string | nil | Category id: item only visible when this category is expanded |
min |
number | 0 | Minimum value |
max |
number | 100 | Maximum value |
step |
number | 1 | Step increment |
value |
number | 0 | Initial value |
infoData |
table | nil | Array of { label, value } pairs shown in the info side panel when focused |
onChange |
function | nil | Callback receiving the new value |
Inserts a visual divider line between menu items. Optional second argument for category.
zed.AddSeparator("main")
-- Separator only visible when category "actions" is expanded
zed.AddSeparator("main", { category = "actions" })| Option | Type | Default | Description |
|---|---|---|---|
category |
string | nil | Category id: separator only visible when this category is expanded |
Categories let you group items that appear only when the category is expanded (e.g. "Actions on player" → Freeze, Kick, Ban). Different from submenus: the category expands in-place; no new menu screen.
-- Add the category header (id is used when assigning items to this category)
zed.AddCategory("main", {
label = "Actions on player",
id = "actions_player",
icon = "bolt",
})
-- Add items that belong to this category (they are hidden until the category is opened)
zed.AddButton("main", {
label = "Freeze",
icon = "snowflake",
category = "actions_player",
onSelect = function() -- ... end,
})
zed.AddButton("main", { label = "Kick", category = "actions_player", onSelect = function() end })
zed.AddButton("main", { label = "Ban", category = "actions_player", onSelect = function() end })| Option | Type | Required | Description |
|---|---|---|---|
label |
string | yes | Display label for the category header |
id |
string | yes | Category id — use this in opts.category when adding items |
icon |
string | no | FontAwesome icon name or image URL |
disabled |
boolean | no | Whether the category header is disabled (default: false) |
Any menu item (button, checkbox, list, slider, submenu, search, info, separator) can have category = "id" so it is only visible when that category is expanded.
Shows a side panel with label/value pairs when the item is focused or hovered. Does nothing on select (no action). Ideal for player details, entity info, etc.
zed.AddInfoButton("main", {
label = "Player info",
icon = "id-card",
data = {
{ label = "Name", value = "John Doe" },
{ label = "Group", value = "Admin" },
{ label = "Job", value = "Police" },
{ label = "Money", value = "$12,500" },
}
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Display label for the info button |
icon |
string | nil | FontAwesome icon name or image URL |
id |
string | auto | Custom unique identifier |
disabled |
boolean | false | Whether the info button is disabled |
category |
string | nil | Category id: item only visible when this category is expanded |
data |
table | — | Array of { label, value } pairs shown in the side panel |
Tip: You don't need a dedicated
AddInfoButtonto show an info panel. All item types (button, checkbox, submenu, list, slider, category) support aninfoDataoption that shows the same side panel when focused:
zed.AddSubMenu("main", "player_actions", "John Doe", {
icon = "user",
infoData = {
{ label = "Name", value = "John Doe" },
{ label = "Group", value = "Admin" },
{ label = "Job", value = "Police" },
{ label = "Money", value = "$12,500" },
}
})
zed.AddButton("main", {
label = "Sell Vehicle",
icon = "car",
rightLabel = "$50,000",
rightLabelColor = "#22c55e",
infoData = {
{ label = "Model", value = "Zentorno" },
{ label = "Plate", value = "ZED-1337" },
{ label = "Fuel", value = "87%" },
},
onSelect = function() end
})Adds a search/filter input that filters menu items in real-time by label.
zed.AddSearchButton("main", {
label = "Search",
icon = "magnifying-glass",
placeholder = "Type to filter..."
})| Option | Type | Default | Description |
|---|---|---|---|
label |
string | "Rechercher" |
Display text |
icon |
string | "magnifying-glass" |
FontAwesome icon name |
placeholder |
string | "Tapez pour rechercher..." |
Placeholder text when active |
id |
string | auto | Custom unique identifier |
category |
string | nil | Category id: item only visible when this category is expanded |
Submenus are created by registering a child menu and linking it to a parent.
-- Create the parent menu
zed.CreateMenu("main", "Main Menu")
-- Add a submenu link (this also registers the submenu automatically)
zed.AddSubMenu("main", "settings", "Settings", {
icon = "gear",
subtitle = "Configure your preferences",
color = "#e74c3c",
})
-- Now add items to the submenu using its ID
zed.AddCheckbox("settings", {
label = "Enable Sounds",
checked = true,
onChange = function(checked)
zed.SetConfig({ sounds = checked })
end
})
zed.AddButton("settings", {
label = "Reset Defaults",
icon = "rotate",
onSelect = function()
zed.Notify({ title = "Reset", message = "Settings restored to defaults" })
end
})
-- Open the parent menu
zed.OpenMenu("main")AddSubMenu options:
| Option | Type | Default | Description |
|---|---|---|---|
icon |
string | nil | FontAwesome icon name or image URL |
id |
string | auto | Custom identifier for the submenu link item |
disabled |
boolean | false | Prevents interaction when true |
category |
string | nil | Category id: item only visible when this category is expanded |
subtitle |
string | label | Title override for the submenu header |
color |
string | nil | Accent color override (hex) |
banner |
string | nil | Banner image URL override |
infoData |
table | nil | Array of { label, value } pairs shown in the info side panel when focused |
-- Open a menu by its ID
zed.OpenMenu("main")
-- Close the currently open menu
zed.CloseMenu()
-- Check if any menu is currently open
if zed.IsMenuOpen() then
print("A menu is open")
end
-- Remove a menu and free its resources
zed.RemoveMenu("main")The context menu system allows players to interact with entities by holding ALT and left-clicking on them. Options are registered by entity type, specific entity, or model name. Submenus expand as flyout panels to the right on hover.
- Player holds ALT — the native cursor appears, the camera locks, but movement (WASD) remains active
- Entities under the cursor get highlighted (semi-transparent) with a hand cursor
- Player left-clicks on a highlighted entity — the context menu opens at the cursor position
- Player selects an option — the callback is executed with the entity handle, type, and coordinates
- Releasing ALT at any time closes everything (cursor, highlight, menu)
Register options that appear for all entities of a given type:
-- Option for all vehicles
zed.AddContextOption({
type = "vehicle",
label = "Lock / Unlock",
icon = "lock",
onSelect = function(entity, entityType, coords)
local locked = GetVehicleDoorLockStatus(entity)
SetVehicleDoorsLocked(entity, locked == 1 and 2 or 1)
end
})
-- Option for all entity types
zed.AddContextOption({
type = "all",
label = "Inspect",
icon = "magnifying-glass",
onSelect = function(entity, entityType, coords)
print("Entity:", entity, "Type:", entityType)
end
})Available types: vehicle, ped, player, object, myself, mycar, all
Options that appear when targeting your own character:
zed.AddContextOption({
type = "myself",
label = "Play Animation",
icon = "person-walking",
onSelect = function(entity, entityType, coords)
-- Open animation menu
end
})
zed.AddContextOption({
type = "myself",
label = "Change Outfit",
icon = "shirt",
onSelect = function(entity, entityType, coords)
-- Open clothing menu
end
})Options that appear when targeting the vehicle you are currently sitting in. These inherit all vehicle type options as well:
zed.AddContextOption({
type = "mycar",
label = "Toggle Engine",
icon = "power-off",
onSelect = function(entity, entityType, coords)
SetVehicleEngineOn(entity, not GetIsVehicleEngineRunning(entity), false, true)
end
})
zed.AddContextOption({
type = "mycar",
label = "Toggle Doors Lock",
icon = "lock",
onSelect = function(entity, entityType, coords)
local locked = GetVehicleDoorLockStatus(entity)
SetVehicleDoorsLocked(entity, locked == 1 and 2 or 1)
end
})Bind options to a single entity handle. Useful for unique NPCs, spawned objects, etc.:
local shopkeeper = CreatePed(4, GetHashKey("a_m_m_indian_01"), 24.5, -1345.6, 29.5, 0.0, true, true)
zed.AddContextOption({
entity = shopkeeper,
label = "Talk to shopkeeper",
icon = "comment",
onSelect = function(entity, entityType, coords)
-- Open shop dialog
end
})
zed.AddContextOption({
entity = shopkeeper,
label = "Browse goods",
icon = "store",
onSelect = function(entity, entityType, coords)
-- Open shop menu
end
})Bind options to all instances of a specific model. Accepts a model name (string) or hash (number):
-- All ATMs in the world
zed.AddContextOption({
model = "prop_atm_01",
label = "Withdraw money",
icon = "money-bill",
onSelect = function(entity, entityType, coords)
-- Open ATM UI
end
})
zed.AddContextOption({
model = "prop_atm_01",
label = "Check balance",
icon = "receipt",
onSelect = function(entity, entityType, coords)
-- Show balance
end
})
-- Also works with a hash
zed.AddContextOption({
model = GetHashKey("prop_vend_snak_01"),
label = "Buy snack",
icon = "cookie-bite",
onSelect = function(entity, entityType, coords)
-- Buy food
end
})Group related options into submenus that expand to the right on hover. Nested submenus are supported:
-- Create a submenu for vehicle actions
zed.AddContextSubMenu({
type = "vehicle",
id = "veh_actions",
label = "Vehicle Actions",
icon = "car"
})
-- Add options inside the submenu
zed.AddContextOption({
type = "vehicle",
submenu = "veh_actions",
label = "Repair",
icon = "wrench",
onSelect = function(entity, entityType, coords)
SetVehicleFixed(entity)
SetVehicleDeformationFixed(entity)
end
})
zed.AddContextOption({
type = "vehicle",
submenu = "veh_actions",
label = "Delete",
icon = "trash",
onSelect = function(entity, entityType, coords)
DeleteVehicle(entity)
end
})
-- Submenus also work with entity or model targeting
zed.AddContextSubMenu({
model = "prop_atm_01",
id = "atm_actions",
label = "ATM Options",
icon = "building-columns"
})| Field | Type | Default | Description |
|---|---|---|---|
type |
string |
'all' |
Entity type: 'vehicle', 'ped', 'player', 'object', 'myself', 'mycar', 'all' |
entity |
number |
nil |
Specific entity handle (overrides type) |
model |
string or number |
nil |
Model name or hash — applies to all props with this model (overrides type) |
label |
string |
required | Display label |
icon |
string |
nil |
FontAwesome icon name or image URL |
id |
string |
auto | Custom identifier |
disabled |
boolean |
false |
Whether the option is grayed out |
submenu |
string |
nil |
Submenu id to nest the option inside |
onSelect |
function |
nil |
function(entity, entityType, coords) called on click |
Priority:
entity>model>type. Ifentityis set,typeandmodelare ignored.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string |
'all' |
Entity type filter |
entity |
number |
nil |
Specific entity handle (overrides type) |
model |
string or number |
nil |
Model name or hash (overrides type) |
id |
string |
auto | Unique submenu identifier |
label |
string |
required | Display label for the submenu |
icon |
string |
nil |
FontAwesome icon name or URL |
-- Remove a specific option by its id
zed.RemoveContextOption("opt_ctx_1")
-- Remove all options and submenus
zed.ClearContext()
-- Disable/enable targeting (ALT hold)
zed.SetContextEnabled(false)
zed.SetContextEnabled(true)
-- Check state
zed.IsContextEnabled() -- boolean
zed.IsContextOpen() -- boolean
-- Force close the context menu
zed.CloseContext()When a resource that registered context options is stopped or restarted, all its options and submenus are automatically removed. No manual cleanup needed — no duplicates on restart.
The progress bar is a blocking function that displays a progress indicator at the bottom of the screen. It returns true if completed or false if cancelled. Supports animations, prop attachments, and selective control disabling.
if zed.ProgressBar({
duration = 3000,
label = 'Repairing vehicle...',
}) then
print('Repair complete!')
else
print('Repair cancelled')
endif zed.ProgressBar({
duration = 2000,
label = 'Drinking water',
canCancel = true,
disable = {
car = true,
combat = true,
},
anim = {
dict = 'mp_player_intdrink',
clip = 'loop_bottle',
},
prop = {
model = 'prop_ld_flow_bottle',
pos = vec3(0.03, 0.03, 0.02),
rot = vec3(0.0, 0.0, -1.5),
},
}) then
print('Finished drinking')
else
print('Stopped drinking')
end| Field | Type | Default | Description |
|---|---|---|---|
duration |
number | 5000 | Duration in milliseconds |
label |
string | '' |
Text displayed above the progress bar |
canCancel |
boolean | true | Whether the player can cancel with ESC or Backspace |
anim |
table | nil | Animation to play: { dict, clip, flag? } |
prop |
table | nil | Prop to attach: { model, pos?, rot?, bone? } |
disable |
table | nil | Controls to disable: { move?, car?, combat?, mouse? } |
anim fields:
| Field | Type | Default | Description |
|---|---|---|---|
dict |
string | — | Animation dictionary |
clip |
string | — | Animation clip name |
flag |
number | 49 | Animation flag |
prop fields:
| Field | Type | Default | Description |
|---|---|---|---|
model |
string/number | — | Prop model name or hash |
pos |
vector3 | (0,0,0) | Offset position relative to the bone |
rot |
vector3 | (0,0,0) | Rotation relative to the bone |
bone |
number | 60309 | Bone index (default: right hand) |
disable fields:
| Field | Type | Default | Description |
|---|---|---|---|
move |
boolean | false | Disable WASD movement |
car |
boolean | false | Disable vehicle controls |
combat |
boolean | false | Disable combat and firing |
mouse |
boolean | false | Disable camera/mouse |
-- Cancel the active progress bar from code
zed.CancelProgressBar()
-- Check if a progress bar is active
if zed.IsProgressActive() then
print('Progress running')
endDisplay a small interaction prompt at world coordinates (e.g. above a ped or in front of a door). When the player is in range and presses the key, a short animation plays on the key box and your callback runs.
-- Fixed position (e.g. door)
zed.Interact({
coords = vector3(123.0, 456.0, 78.0),
label = "Ouvrir la porte",
key = "E",
distance = 1.5,
onSelect = function()
print("Door opened!")
end,
})
-- Attach to a specific entity (prompt follows the entity)
zed.Interact({
entity = myPed,
label = "Parler au PNJ",
key = "E",
onSelect = function() end,
})
-- Attach to the closest entity of a type (all peds, all vehicles, or all objects in range)
-- onSelect(entity) receives the entity you interacted with (nil if using coords only)
zed.Interact({
type = "ped",
label = "Fouiller",
key = "E",
distance = 2.0,
onSelect = function(entity)
if entity then
-- e.g. TaskStartScenarioInPlace(entity, ...) or DeleteEntity(entity)
end
end,
})
zed.Interact({ type = "vehicle", label = "Ouvrir le coffre", key = "E", onSelect = function(veh) SetVehicleDoorOpen(veh, 5, false, false) end })
zed.Interact({ type = "object", label = "Utiliser", key = "E", onSelect = function() end })If you omit key, only the label is shown (no key box on the left):
zed.Interact({
coords = vector3(0, 0, 0),
label = "Zone d'interaction",
onSelect = function() end,
})| Field | Type | Default | Description |
|---|---|---|---|
coords |
vector3 or fun | — | World position, or function returning vector3. Omit if using entity or type. |
entity |
number | nil | Specific entity handle. Prompt is shown on this entity and follows it. |
type |
string | nil | "ped", "vehicle", or "object". Prompt is shown on the closest entity of this type within distance. |
label |
string | — | Text displayed next to the key |
key |
string | nil | Key to show (e.g. "E"). If omitted, no key box is shown |
distance |
number | 2.0 | Max distance to show the prompt (and for type, search radius for closest entity) |
onSelect |
function | nil | Callback when the player presses the key: onSelect(entity). entity is the targeted entity (nil if using coords only). |
You must set exactly one of coords, entity, or type. Supported key labels: E, G, F, R, Q, Z, X, C, T, Y.
zed.ClearInteract()Same as Interact but the player must hold the key for a given duration. A progress bar appears below the label while holding; onSelect(entity) runs when the duration is complete, and onCancel(entity) runs if they release early or leave range. In both callbacks, entity is the entity the prompt was attached to (nil if using coords only).
zed.InteractProgress({
coords = vector3(x, y, z),
label = "Dévaliser l'individu",
key = "E",
distance = 2.0,
duration = 2000, -- ms to hold
onSelect = function()
print("Completed!")
end,
onCancel = function()
print("Cancelled or released early.")
end,
})
zed.ClearInteractProgress() -- clear when doneTo keep the prompt after the action (e.g. repeat "Dévaliser" on the same ped), set removeOnComplete = false:
zed.InteractProgress({
coords = function() return GetEntityCoords(targetPed) + vector3(0, 0, 1) end,
label = "Dévaliser l'individu",
key = "E",
duration = 2000,
removeOnComplete = false, -- prompt stays, player can do it again
onSelect = function() end,
})| Field | Type | Default | Description |
|---|---|---|---|
coords |
vector3 or fun | — | World position (or function). Omit if using entity or type. |
entity |
number | nil | Specific entity handle. Prompt is shown on this entity. |
type |
string | nil | "ped", "vehicle", or "object". Prompt on the closest entity of this type in range. |
label |
string | — | Text displayed above the progress bar |
key |
string | nil | Key to show (e.g. "E"). Omit for no key box |
distance |
number | 2.0 | Max distance to show the prompt |
duration |
number | — | Time in ms the player must hold the key |
removeOnComplete |
boolean | true | If true, the prompt is removed after the action. If false, it stays so the player can repeat. |
onSelect |
function | nil | Called when the player holds for the full duration: onSelect(entity) |
onCancel |
function | nil | Called when the player releases before completion or leaves range: onCancel(entity) |
All notification functions take a single data table. You only pass the fields you need; no need for nil placeholders.
-- Generic notification (specify type in data)
zed.Notify({
type = "success",
title = "Purchase Complete",
message = "You bought a Zentorno for $725,000",
duration = 5000
})
-- With subtitle, color and image (omit what you don't need)
zed.Notify({
type = "info",
title = "Custom",
subtitle = "Category",
message = "Message here",
duration = 5000,
color = "#3498db",
image = "https://example.com/icon.png"
})| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title |
string | yes | — | Notification title |
type |
string | no | "info" |
One of "success", "error", "warning", "info" |
subtitle |
string | no | — | Subtitle below the title |
message |
string | no | — | Body text (description) |
duration |
number | no | 5000 | Display time in milliseconds |
color |
string | no | — | Accent color (hex, e.g. "#e74c3c") |
image |
string | no | — | Image URL shown at top-left of the notification |
zed.ClearNotifications()From the server, you can send notifications to a specific player or broadcast to all players using exports. No event is exposed for clients to trigger broadcasts (prevents abuse).
Server-side (in a server_scripts file or from another resource):
-- Send to a specific player (by server ID)
exports['zedlib']:Notify(source, {
title = "Welcome",
type = "info",
message = "You are now connected.",
duration = 5000
})
-- Broadcast to all players
exports['zedlib']:NotifyToAll({
title = "Announcement",
subtitle = "Server",
message = "Restart in 5 minutes.",
type = "warning",
duration = 10000
})| Export | Parameters | Description |
|---|---|---|
Notify(source, data) |
source = server ID, data = notification table |
Send a notification to a specific player |
NotifyToAll(data) |
data = same table as above |
Broadcast a notification to all connected players |
The data fields are the same as client-side notifications (see table above). Only the server can call these exports.
Create a dialog with input fields and action buttons. The dialog captures NUI focus automatically.
zed.Dialog({
title = "Spawn Vehicle",
message = "Enter the vehicle details below",
type = "input",
closable = true,
inputs = {
{
id = "model",
type = "text",
label = "Model Name",
placeholder = "adder",
required = true,
maxLength = 32
},
{
id = "color",
type = "text",
label = "Color (hex)",
placeholder = "#FF0000",
},
{
id = "amount",
type = "number",
label = "Quantity",
default = 1,
min = 1,
max = 10
},
},
buttons = {
{
label = "Cancel",
variant = "secondary",
action = "cancel",
},
{
label = "Spawn",
variant = "primary",
action = "spawn",
onPress = function(values)
print("Model:", values.model)
print("Color:", values.color)
print("Amount:", values.amount)
end
},
},
})Dialog options:
| Option | Type | Default | Description |
|---|---|---|---|
id |
string | auto | Custom dialog identifier |
type |
string | "input" |
"input" or "confirm" |
title |
string | — | Dialog title |
message |
string | nil | Description text |
inputs |
table | nil | Array of input field definitions |
buttons |
table | nil | Array of button definitions |
closable |
boolean | true | Whether the dialog can be dismissed with Escape |
color |
string | "#e74c3c" |
Accent color (hex) for header bar and primary button |
icon |
string | nil | FontAwesome icon name in dialog header (e.g. "circle-question") |
onResult |
function | nil | Global callback receiving (action, values) for any button press |
Input field options:
| Option | Type | Default | Description |
|---|---|---|---|
id |
string | "input_N" |
Field identifier (key in values table) |
type |
string | "text" |
"text", "number", "password", or "textarea" |
label |
string | — | Field label |
placeholder |
string | nil | Placeholder text |
default |
any | nil | Default value |
required |
boolean | false | Whether the field must be filled |
maxLength |
number | nil | Maximum character length |
min |
number | nil | Minimum value (number fields only) |
max |
number | nil | Maximum value (number fields only) |
Button options:
| Option | Type | Default | Description |
|---|---|---|---|
label |
string | — | Button text |
variant |
string | "secondary" |
"primary", "secondary", or "danger" |
action |
string | auto | Action identifier sent to callbacks |
icon |
string | nil | FontAwesome icon name before the label (e.g. "trash") |
onPress |
function | nil | Callback receiving the values table |
You can handle all button actions in a single callback:
zed.Dialog({
title = "Delete Character",
message = "This action is irreversible. Are you sure?",
type = "confirm",
buttons = {
{ label = "Keep", variant = "secondary", action = "cancel" },
{ label = "Delete", variant = "danger", action = "delete" },
},
onResult = function(action, values)
if action == "delete" then
print("Character deleted")
else
print("Cancelled")
end
end
})A one-liner shortcut for simple yes/no confirmations:
zed.Confirm("Delete Vehicle?", "This will permanently remove your vehicle.",
function()
print("User confirmed")
DeleteVehicle(GetVehiclePedIsIn(PlayerPedId(), true))
end,
function()
print("User cancelled")
end
)| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | yes | Dialog title |
message |
string | yes | Dialog message |
onConfirm |
function | no | Callback fired when the user confirms |
onCancel |
function | no | Callback fired when the user cancels |
color |
string | no | Accent color (hex) for the dialog |
zed.CloseDialog()ZedLib has a config.lua file at the root of the resource for static settings. Edit this file and restart the resource to apply changes.
-- zedlib/config.lua
ZedConfig = {
accentColor = '#e74c3c', -- Global accent color for all components (hex)
showTitle = true, -- Show the menu title in the header
showItemCount = true, -- Show the item counter (e.g. "3 / 12") in the header
enableContextMenu = true, -- Enable the context menu system (ALT targeting)
}| Option | Type | Default | Description |
|---|---|---|---|
accentColor |
string | '#e74c3c' |
Global accent color applied to menus, notifications, dialogs, and context menus. Per-component colors (e.g. menu color option, notification color field) override this. |
showTitle |
boolean | true | Show menu title in the header (both banner and non-banner menus) |
showItemCount |
boolean | true | Show the item counter (e.g. 3 / 12) in the header |
enableContextMenu |
boolean | true | Enable the ALT-hold context menu targeting system |
Toggle settings dynamically at runtime from any script:
zed.SetConfig({
sounds = false -- disable all UI sounds
})| Option | Type | Default | Description |
|---|---|---|---|
sounds |
boolean | true | Enable or disable UI sound effects (hover, select, toggle) |
When a menu is open, the following controls are active. Weapon and attack controls are automatically disabled.
| Key / Input | Action |
|---|---|
Arrow Up |
Move selection up |
Arrow Down |
Move selection down |
| Mouse wheel up | Move selection up |
| Mouse wheel down | Move selection down |
Enter |
Select / Toggle current item |
Arrow Left |
Previous option (list) / Decrease (slider) |
Arrow Right |
Next option (list) / Increase (slider) |
Backspace |
Go back to parent menu |
Escape |
Close the menu entirely |
Hold arrow keys for auto-repeat (300ms initial delay, 80ms repeat interval). The mouse wheel can also be used to move the selection when the menu is open.
| Function | Parameters | Returns | Description |
|---|---|---|---|
CreateMenu |
id, title, subtitle?, opts? |
— | Register a new menu |
AddButton |
menuId, opts |
itemId |
Add a button item |
AddCheckbox |
menuId, opts |
itemId |
Add a checkbox item |
AddList |
menuId, opts |
itemId |
Add a list selector |
AddSlider |
menuId, opts |
itemId |
Add a slider |
AddSeparator |
menuId, opts? |
itemId |
Add a visual separator (opts: category) |
AddCategory |
menuId, opts |
categoryId |
Add a category header (expand/collapse items) |
AddSearchButton |
menuId, opts? |
itemId |
Add a search/filter input |
AddInfoButton |
menuId, opts |
itemId |
Add an info button (hover panel with label/value) |
AddSubMenu |
menuId, subMenuId, label, opts? |
subMenuId |
Link and register a submenu |
OpenMenu |
id |
— | Open a menu |
CloseMenu |
— | — | Close the current menu |
IsMenuOpen |
— | boolean |
Check if a menu is open |
RemoveMenu |
id |
— | Remove a menu and free resources |
| Function | Parameters | Returns | Description |
|---|---|---|---|
Notify |
data: { title, type?, subtitle?, message?, duration?, color?, image? } |
— | Show a notification |
ClearNotifications |
— | — | Dismiss all notifications |
Server exports: exports['zedlib']:Notify(source, data) for a specific player, exports['zedlib']:NotifyToAll(data) for all. See Server-side Notifications.
| Function | Parameters | Returns | Description |
|---|---|---|---|
Dialog |
opts |
dialogId |
Open an input/custom dialog (opts: color, icon, inputs type textarea, button icon) |
Confirm |
title, message, onConfirm?, onCancel?, color? |
— | Open a quick confirm dialog |
CloseDialog |
— | — | Close the active dialog |
| Function | Parameters | Returns | Description |
|---|---|---|---|
AddContextOption |
opts |
optionId |
Register an option (by type, entity, or model) |
AddContextSubMenu |
opts |
submenuId |
Register a submenu (by type, entity, or model) |
RemoveContextOption |
id |
— | Remove a context option by id |
ClearContext |
— | — | Remove all options and submenus |
SetContextEnabled |
enabled |
— | Enable or disable ALT targeting |
IsContextEnabled |
— | boolean |
Check if targeting is enabled |
IsContextOpen |
— | boolean |
Check if a context menu is visible |
CloseContext |
— | — | Force close the context menu |
Options support type (entity type filter), entity (specific entity handle), or model (model name/hash). Auto-cleanup on resource stop.
| Function | Parameters | Returns | Description |
|---|---|---|---|
ProgressBar |
opts |
boolean |
Show a blocking progress bar. Returns true if completed, false if cancelled |
CancelProgressBar |
— | — | Cancel the active progress bar |
IsProgressActive |
— | boolean |
Check if a progress bar is running |
| Function | Parameters | Returns | Description |
|---|---|---|---|
Interact |
opts |
— | Set the current interact prompt (coords, label, key?, distance?, onSelect?) |
ClearInteract |
— | — | Clear the current interact prompt |
InteractProgress |
opts |
— | Set hold-to-complete prompt (duration, onSelect?, onCancel?) |
ClearInteractProgress |
— | — | Clear the current interact progress prompt |
| Function | Parameters | Returns | Description |
|---|---|---|---|
SetConfig |
opts |
— | Update runtime configuration |
| Function | Parameters | Returns | Description |
|---|---|---|---|
CopyToClipboard |
text |
— | Copy text to the player's clipboard |
zed.CopyToClipboard("Hello world!")A full working example combining menus, submenus, notifications, and dialogs:
-- In your resource's client.lua (with @zedlib/import.lua loaded)
RegisterCommand("mymenu", function()
-- Create menus
zed.CreateMenu("main", "Admin Panel", "Server Administration", {
banner = "https://forum-cfx-re.akamaized.net/original/4X/d/1/2/d12e827f6eba4f2b64c78dc10895aefe4eaffd4e.jpeg",
color = "#e74c3c"
})
-- Player actions
zed.AddButton("main", {
label = "Heal Player",
icon = "heart",
description = "Restore full health",
onSelect = function()
SetEntityHealth(PlayerPedId(), 200)
zed.Notify({ title = "Healed", message = "Health restored to full" })
end
})
zed.AddCheckbox("main", {
label = "Invisible",
icon = "eye-slash",
checked = false,
onChange = function(checked)
SetEntityVisible(PlayerPedId(), not checked, false)
zed.Notify({ title = "Visibility", message = checked and "You are now invisible" or "You are now visible" })
end
})
zed.AddSeparator("main")
-- Vehicle submenu
zed.AddSubMenu("main", "vehicles", "Vehicles", {
icon = "car",
subtitle = "Vehicle Management"
})
zed.AddButton("vehicles", {
label = "Spawn Vehicle",
icon = "plus",
onSelect = function()
zed.CloseMenu()
zed.Dialog({
title = "Spawn Vehicle",
message = "Enter a vehicle model name",
inputs = {
{ id = "model", type = "text", label = "Model", placeholder = "adder", required = true }
},
buttons = {
{ label = "Cancel", variant = "secondary", action = "cancel" },
{
label = "Spawn",
variant = "primary",
action = "spawn",
onPress = function(values)
print("Spawning:", values.model)
end
}
}
})
end
})
zed.AddList("vehicles", {
label = "Vehicle Color",
icon = "palette",
items = {
{ label = "Red", value = 27 },
{ label = "Blue", value = 64 },
{ label = "Green", value = 55 },
{ label = "Black", value = 0 },
{ label = "White", value = 134 },
},
currentIndex = 1,
onChange = function(index, item)
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
if veh ~= 0 then
SetVehicleColours(veh, item.value, item.value)
end
end
})
zed.AddSlider("vehicles", {
label = "Max Speed",
icon = "gauge-high",
min = 50,
max = 300,
step = 25,
value = 150,
onChange = function(value)
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
if veh ~= 0 then
SetVehicleMaxSpeed(veh, value + 0.0)
end
end
})
-- Danger zone submenu
zed.AddSubMenu("main", "danger", "Danger Zone", {
icon = "triangle-exclamation",
color = "#c0392b"
})
zed.AddButton("danger", {
label = "Delete Nearby Vehicles",
icon = "trash",
onSelect = function()
zed.CloseMenu()
zed.Confirm("Delete Vehicles?", "This will remove all vehicles in a 50m radius.",
function()
zed.Notify({ title = "Deleted", message = "Nearby vehicles have been removed" })
end,
function()
zed.Notify({ title = "Cancelled", message = "No vehicles were deleted" })
end
)
end
})
-- Search (useful for long menus)
zed.AddSearchButton("main", {
label = "Search",
placeholder = "Filter options..."
})
zed.OpenMenu("main")
end)Start the Vite dev server for local UI development without FiveM:
cd zedlib/web
npm install
npm run devThis opens a dev server with a Playground panel for testing all components interactively.
cd zedlib/web
npm run buildOutput goes to web/dist/, referenced by fxmanifest.lua.
zedlib/
├── fxmanifest.lua # FiveM resource manifest
├── config.lua # Static configuration (title, item count, context menu)
├── import.lua # zed.* API wrapper with cross-resource callback support
├── lua/
│ ├── client.lua # NUI bridge, keyboard controls, callback routing
│ └── api/ # Lua API split by component
│ ├── _init.lua # UI table, ZedInternal (generateId, fireCallback)
│ ├── menu.lua # CreateMenu, AddButton, AddCheckbox, AddCategory, AddInfoButton, etc.
│ ├── notification.lua # Notify, ClearNotifications
│ ├── dialog.lua # Dialog, Confirm, CloseDialog
│ ├── context.lua # AddContextOption, AddContextSubMenu, entity/model targeting
│ ├── progressbar.lua # ProgressBar, CancelProgressBar, IsProgressActive
│ ├── interact.lua # SetInteract, ClearInteract, SetInteractProgress, ClearInteractProgress
│ ├── config.lua # SetConfig
│ └── exports.lua # FiveM exports for all functions
├── lua/events/
│ └── notification.lua # Server-side Notify, NotifyToAll exports
└── web/
├── package.json
├── src/
│ ├── main.tsx # React entry point
│ ├── App.tsx # Root component
│ ├── core/ # EventBus, PluginSystem, Registry, Sounds
│ ├── components/ # Menu, Dialog, Notification (menu: category, info panel)
│ ├── stores/ # Zustand state stores
│ ├── hooks/ # useNui, useKeyboard, useMenu
│ ├── nui/ # NUI bridge and message handlers
│ ├── types/ # TypeScript type definitions
│ └── devtools/ # Dev playground
└── dist/ # Production build output
MIT