Volver al blog
Tutorial9 min de lectura

Guía de ox_lib: la biblioteca imprescindible de FiveM para desarrolladores

Aprende ox_lib desde cero: notificaciones, menús contextuales, callbacks, zonas y exports esenciales para scripts modernos de FiveM, con ejemplos reales.

Agency Scripts

Fundador y desarrollador principal en Agency Scripts

¿Qué es ox_lib y por qué deberías usarlo?

ox_lib es una biblioteca de utilidades de código abierto para FiveM que se ha convertido en el estándar de facto para el desarrollo de scripts modernos. Incluye una amplia colección de componentes de interfaz, funciones de utilidad y herramientas de rendimiento listas para usar, que evitan que tengas que reinventar la rueda en cada script que escribas. Antes de que existiera ox_lib, los desarrolladores tenían que crear desde cero sus propios sistemas de notificaciones, diálogos de entrada, barras de progreso y menús contextuales, lo que a menudo daba lugar a interfaces incoherentes entre los distintos scripts de un mismo servidor. ox_lib soluciona esto al ofrecer un conjunto unificado y refinado de componentes que tienen un aspecto profesional y funcionan de forma fiable desde el primer momento. Es compatible con Lua y JavaScript, funciona con cualquier framework (QBCore, ESX o standalone) y el equipo de Overextended lo mantiene activamente. Si en 2026 escribes scripts para FiveM y no usas ox_lib, estás perdiendo tiempo innecesariamente en crear cosas que ya existen.

Configuración de ox_lib en tu recurso

Añadir ox_lib a tu recurso requiere solo dos pasos: agregar la dependencia a tu fxmanifest.lua y llamar a la biblioteca en tus scripts. La @ox_lib/init.lua import te da acceso a todas las utilidades compartidas, mientras que el sistema de módulos te permite cargar selectivamente solo las funciones que necesitas. Esto mantiene tu recurso ligero, porque los módulos que no se usan nunca se cargan. Asegúrate de iniciar ox_lib antes que tu recurso en tu server.cfg, colocando ensure ox_lib por encima de tus recursos personalizados. Esta es la configuración mínima para un recurso nuevo que usa ox_lib:

-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

name 'my-awesome-script'
version '1.0.0'

-- Required: import ox_lib
shared_scripts {
    '@ox_lib/init.lua',
    'config.lua',
}

client_scripts {
    'client/*.lua',
}

server_scripts {
    'server/*.lua',
}

-- Declare ox_lib as a dependency
dependencies {
    'ox_lib',
}

-- Enable ox_lib locale system (optional)
lua54 'yes'

Notificaciones: alertas limpias y coherentes

Las notificaciones de ox_lib reemplazan los mensajes de chat predeterminados poco atractivos y las ventanas emergentes NUI personalizadas que usan la mayoría de los scripts. Aparecen como mensajes toast elegantes, con iconos, colores y cierre automático. Puedes establecer la posición, la duración y el tipo (éxito, error, advertencia, información), e incluso añadir una descripción debajo del título. El sistema de notificaciones funciona solo del lado del cliente y es increíblemente ligero, por lo que prácticamente no añade carga a tu script. Las notificaciones son la función de ox_lib más utilizada y deberían ser tu forma predeterminada de comunicar información a los jugadores en cualquier script que crees.

-- client.lua: Notification examples

-- Simple notification
lib.notify({
    title = 'Vehicle Stored',
    description = 'Your vehicle has been stored in the garage.',
    type = 'success',         -- 'success' | 'error' | 'warning' | 'info'
    duration = 5000,          -- milliseconds
    position = 'top-right',   -- 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
})

-- Error notification with icon
lib.notify({
    title = 'Access Denied',
    description = 'You do not have the required key.',
    type = 'error',
    icon = 'lock',
    iconColor = '#ff4444',
})

-- Notification from server side
-- server.lua
RegisterNetEvent('garage:store', function()
    local src = source
    TriggerClientEvent('ox_lib:notify', src, {
        title = 'Garage',
        description = 'Vehicle stored successfully.',
        type = 'success',
    })
end)

Menús contextuales: listas de opciones interactivas

Los menús contextuales son listas desplazables de opciones que los jugadores pueden pulsar para activar acciones. Son ideales para menús de trabajo, interfaces de tiendas, opciones de vehículos y cualquier situación en la que un jugador tenga que elegir entre varias acciones. Cada elemento del menú puede incluir un icono, una descripción, metadatos que se muestran a la derecha y submenús anidados para organizar árboles de opciones complejos. El menú permanece abierto hasta que el jugador lo cierra expresamente o selecciona una opción que no abre un submenú, por lo que resulta ideal para explorar categorías de objetos. Los menús contextuales también se pueden generar dinámicamente a partir de datos del servidor, así puedes crear menús de tiendas que reflejen el inventario de tu base de datos en tiempo real.

-- client.lua: Context menu examples

-- Simple shop menu
lib.registerContext({
    id = 'weapons_shop',
    title = 'Ammu-Nation',
    options = {
        {
            title = 'Pistol',
            description = 'Standard 9mm handgun',
            icon = 'gun',
            metadata = {
                {label = 'Price', value = '$2,500'},
                {label = 'Ammo', value = '12 rounds'},
            },
            onSelect = function()
                TriggerServerEvent('shop:buy', 'weapon_pistol')
            end,
        },
        {
            title = 'Body Armor',
            description = 'Standard kevlar vest',
            icon = 'shield',
            metadata = {
                {label = 'Price', value = '$5,000'},
                {label = 'Protection', value = '50%'},
            },
            onSelect = function()
                TriggerServerEvent('shop:buy', 'armor')
            end,
        },
        {
            title = 'Attachments',
            description = 'Browse weapon modifications',
            icon = 'wrench',
            arrow = true,  -- Shows arrow indicating submenu
            menu = 'attachments_submenu',
        },
    },
})

lib.showContext('weapons_shop')

Barras y círculos de progreso

Las barras de progreso ofrecen información visual durante acciones cronometradas, como forzar cerraduras, crear objetos, reparar vehículos o cocinar. ox_lib proporciona tanto una barra lineal como un indicador circular. Durante la animación de progreso, puedes desactivar controles del jugador, como el movimiento, el combate y el acceso a vehículos, para evitar exploits. También puedes asignar una animación y un objeto al jugador para que realice la acción de forma visible mientras se llena la barra. La función devuelve true si el jugador canceló la acción (por ejemplo, al moverse) y false si se completó correctamente. Comprueba siempre este valor de retorno para evitar conceder objetos o completar acciones que el jugador interrumpió.

-- client.lua: Progress bar examples

-- Linear progress bar with animation
local cancelled = lib.progressBar({
    duration = 8000,
    label = 'Lockpicking door...',
    useWhileDead = false,
    canCancel = true,
    disable = {
        car = true,
        move = true,
        combat = true,
    },
    anim = {
        dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@',
        clip = 'machinic_loop_mechandler',
    },
    prop = {
        model = 'prop_lockpick_01',
        bone = 57005,
        pos = vec3(0.14, 0.0, -0.01),
        rot = vec3(0.0, 0.0, 0.0),
    },
})

if cancelled then
    lib.notify({ title = 'Cancelled', type = 'error' })
else
    lib.notify({ title = 'Door Unlocked', type = 'success' })
    TriggerServerEvent('lockpick:success', doorId)
end

-- Circular progress (useful for quick actions)
if lib.progressCircle({
    duration = 2000,
    label = 'Searching...',
    position = 'bottom',
    useWhileDead = false,
    canCancel = true,
    disable = { move = true },
}) then
    lib.notify({ title = 'Search cancelled', type = 'error' })
else
    TriggerServerEvent('search:complete')
end

Diálogos de entrada: recopilar datos de los jugadores

Los diálogos de entrada te permiten recopilar texto, números, selecciones de listas desplegables, casillas de verificación, selectores de color, fechas y valores de controles deslizantes de los jugadores mediante una interfaz modal sencilla. Son esenciales para los scripts que necesitan datos de los jugadores, como establecer el precio de una casa, introducir una matrícula, ponerle nombre a una banda o configurar ajustes de trabajo. Cada campo de entrada tiene una etiqueta, una descripción opcional, un indicador de obligatoriedad y opciones específicas según el tipo, como valores mínimos y máximos para los números o opciones predefinidas para las listas desplegables. La función devuelve nil si el jugador cancela el diálogo y un array de valores en el orden de los campos si lo envía. Valida siempre los datos devueltos tanto en el cliente como en el servidor para evitar exploits.

-- client.lua: Input dialog examples

-- Vehicle listing form
local input = lib.inputDialog('List Vehicle for Sale', {
    { type = 'input', label = 'Title', description = 'Name for the listing', required = true, max = 50 },
    { type = 'number', label = 'Price ($)', description = 'Asking price', required = true, min = 1000, max = 10000000 },
    { type = 'select', label = 'Condition', options = {
        { value = 'new', label = 'Brand New' },
        { value = 'used', label = 'Used - Good' },
        { value = 'damaged', label = 'Damaged' },
    }},
    { type = 'textarea', label = 'Description', description = 'Describe your vehicle', max = 500 },
    { type = 'checkbox', label = 'I agree to the marketplace terms' },
})

if not input then return end -- Player cancelled

local title, price, condition, description, agreedTerms = table.unpack(input)

if not agreedTerms then
    lib.notify({ title = 'You must agree to the terms', type = 'error' })
    return
end

TriggerServerEvent('marketplace:list', {
    title = title,
    price = price,
    condition = condition,
    description = description,
})

Zonas: detección eficiente de áreas

Las zonas de ox_lib reemplazan el método antiguo e ineficiente de comprobar la posición del jugador en cada frame con GetEntityCoords y cálculos de distancia. El sistema de zonas utiliza un algoritmo optimizado de detección espacial que solo comprueba las coordenadas en intervalos configurables y activa callbacks de entrada y salida cuando los jugadores cruzan los límites de las zonas. Puedes definir zonas como esferas, cajas o polígonos, lo que las hace lo bastante flexibles para todo tipo de usos, desde pequeños puntos de interacción hasta los límites de grandes vecindarios. Las zonas admiten rotación, visualización de depuración durante el desarrollo y datos arbitrarios que se pasan a los callbacks. Para cualquier script que necesite detectar si un jugador está en un área concreta, las zonas de ox_lib son la solución más eficiente disponible.

-- client.lua: Zone examples

-- Sphere zone for a shop entrance
local shopZone = lib.zones.sphere({
    coords = vec3(25.7, -1347.3, 29.5),
    radius = 3.0,
    debug = true,  -- Set false in production
    onEnter = function(self)
        lib.notify({ title = 'Press [E] to open shop', type = 'info' })
        lib.showTextUI('[E] Open Shop', { position = 'right-center' })
    end,
    onExit = function(self)
        lib.hideTextUI()
    end,
})

-- Box zone with rotation for a parking spot
local parkingZone = lib.zones.box({
    coords = vec3(215.3, -810.0, 30.7),
    size = vec3(6.0, 3.0, 2.0),
    rotation = 70.0,
    debug = true,
    onEnter = function(self)
        lib.showTextUI('[E] Store Vehicle')
    end,
    onExit = function(self)
        lib.hideTextUI()
    end,
})

-- Clean up zones when resource stops
AddEventHandler('onResourceStop', function(resource)
    if resource == GetCurrentResourceName() then
        shopZone:remove()
        parkingZone:remove()
    end
end)

Cache: acceso inteligente a los datos

El lib.cache El módulo ofrece acceso inmediato a los datos de jugador más solicitados sin realizar llamadas nativas en cada frame. Valores como cache.ped, cache.vehicle, cache.seat, cache.weapon, y cache.playerId se mantienen actualizados automáticamente mediante ox_lib con listeners de eventos en lugar de consultas repetidas. Esto significa que puedes leer cache.vehicle en cualquier parte de tu código sin preocuparte por el rendimiento. La cache también activa eventos cuando cambian los valores, así que puedes registrar handlers para ox_lib:cache:vehicle y reaccionar cuando un jugador entra o sale de un vehículo. Junto con las zonas y el resto de ox_lib, el sistema de cache te permite escribir código limpio y basado en eventos, en lugar de bucles de consulta por frame que desperdician ciclos de CPU comprobando condiciones que rara vez cambian.

-- client.lua: Cache examples

-- Access cached values (no native calls needed)
local myPed = cache.ped
local myVehicle = cache.vehicle  -- nil if not in a vehicle
local mySeat = cache.seat        -- -1 = driver, 0 = front passenger, etc.
local myWeapon = cache.weapon

-- React to vehicle changes
lib.onCache('vehicle', function(vehicle)
    if vehicle then
        -- Player entered a vehicle
        local plate = GetVehicleNumberPlateText(vehicle)
        lib.notify({
            title = 'Vehicle',
            description = 'Plate: ' .. plate,
            type = 'info',
        })
    else
        -- Player exited a vehicle
        lib.notify({ title = 'On foot', type = 'info' })
    end
end)

-- React to weapon changes
lib.onCache('weapon', function(weapon)
    if weapon then
        print('Player equipped weapon:', weapon)
    end
end)

¿Listo para empezar?

Consigue los scripts en nuestra tienda, o únete al Discord para soporte, novedades y un adelanto de lo que viene.