Tutorial 2026-04-23

The Complete ox_lib Guide for FiveM Development

TDYSKY

TDYSKY

Founder & Lead Developer at Agency Scripts

What is ox_lib and Why Should You Use It?

ox_lib is an open-source utility library for FiveM that has become the de facto standard for modern script development. It provides a massive collection of pre-built UI components, utility functions, and performance tools that eliminate the need to reinvent the wheel for every script you write. Before ox_lib existed, developers had to build their own notification systems, input dialogs, progress bars, and context menus from scratch, often resulting in inconsistent UI across different scripts on the same server. ox_lib solves this by providing a unified, polished set of components that look professional and work reliably out of the box. It supports both Lua and JavaScript, works with any framework (QBCore, ESX, or standalone), and is actively maintained by the Overextended team. If you are writing FiveM scripts in 2026 and not using ox_lib, you are spending unnecessary time building things that already exist.

Setting Up ox_lib in Your Resource

Getting ox_lib into your resource requires just two steps: adding the dependency to your fxmanifest.lua and calling the library in your scripts. The @ox_lib/init.lua import gives you access to all shared utilities, while the module system lets you selectively load only the features you need. This keeps your resource lightweight because unused modules are never loaded. Make sure ox_lib is started before your resource in your server.cfg by placing ensure ox_lib above your custom resources. Here is the minimal setup for a new resource that uses 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'

Notifications: Clean, Consistent Alerts

ox_lib notifications replace the ugly default chat messages and custom NUI popups that most scripts use. They appear as sleek toast messages with icons, colors, and automatic dismissal. You can set the position, duration, type (success, error, warning, info), and even add a description below the title. The notification system is client-side only and incredibly lightweight, adding virtually zero overhead to your script. Notifications are the most commonly used ox_lib feature and should be your default way to communicate feedback to players in any script you build.

-- 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)

Context Menus: Interactive Option Lists

Context menus are scrollable lists of options that players can click to trigger actions. They are perfect for job menus, shop interfaces, vehicle options, and any scenario where a player needs to choose from multiple actions. Each menu item can have an icon, a description, metadata displayed on the right side, and nested submenus for organizing complex option trees. The menu stays open until the player explicitly closes it or selects a non-submenu option, which makes it ideal for browsing through categories of items. Context menus can also be dynamically generated based on server data, so you can build shop menus that reflect real-time inventory from your database.

-- 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')

Progress Bars and Progress Circles

Progress bars give visual feedback during timed actions like lockpicking, crafting, repairing vehicles, or cooking food. ox_lib provides both a linear bar and a circular indicator. During the progress animation, you can disable player controls like movement, combat, and car entry to prevent exploits. You can also attach an animation and a prop to the player so they visibly perform the action while the bar fills. The function returns true if the player cancelled the action (by moving, for example) and false if it completed successfully. Always check this return value to avoid granting items or completing actions that the player interrupted.

-- 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

Input Dialogs: Collecting Player Data

Input dialogs let you collect typed text, numbers, dropdown selections, checkboxes, color pickers, dates, and slider values from players through a clean modal interface. This is essential for scripts that need player input like setting a house price, entering a license plate, naming a gang, or configuring job settings. Each input field has a label, optional description, required flag, and type-specific options like min/max values for numbers or pre-defined options for dropdowns. The function returns nil if the player cancels the dialog and an array of values in field order if they submit. Always validate the returned data on both client and server to prevent 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,
})

Zones: Efficient Area Detection

ox_lib zones replace the old, inefficient method of checking player position every frame with GetEntityCoords and distance math. The zone system uses an optimized spatial detection algorithm that only checks coordinates at configurable intervals and triggers enter/exit callbacks when players cross zone boundaries. You can define zones as spheres, boxes, or polygons, making them flexible enough for everything from small interaction points to large neighborhood boundaries. Zones support rotation, debug drawing for development, and arbitrary data that gets passed to the callbacks. For any script that needs to detect when a player is in a specific area, ox_lib zones are the most performant solution available.

-- 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: Smart Data Access

The lib.cache module provides instant access to frequently needed player data without making native calls every frame. Values like cache.ped, cache.vehicle, cache.seat, cache.weapon, and cache.playerId are automatically kept up to date by ox_lib through event listeners rather than polling. This means you can safely read cache.vehicle anywhere in your code without worrying about performance. The cache also fires events when values change, so you can register handlers for ox_lib:cache:vehicle to react when a player enters or exits a vehicle. Combined with zones and the rest of ox_lib, the cache system lets you write clean, event-driven code instead of frame-based polling loops that waste CPU cycles checking conditions that rarely change.

-- 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)

Share this article

Ready to upgrade your server?

Check out our premium FiveM scripts in the Agency Scripts store or join our Discord community for support and updates.