Getting started
The Lua you need for FiveM, from zero
Learn Lua for FiveM: variables and local, types, strings, if/else, for and while loops, functions, nil and truthiness, Lua 5.4 integers — plus the CfxLua extras: vectors, backtick hashes, CreateThread, Wait and json.
Overview
You do not need to master Lua to write FiveM scripts, but you do need the core of it: how values and variables behave, how to branch and loop, and how functions work. This guide teaches exactly that, with FiveM-flavoured examples, and then covers the extras FiveM adds on top of standard Lua 5.4.
Variables and local
local money = 500 -- a number
local name = 'Mike' -- a string
local onDuty = false -- a boolean
local vehicle = nil -- nothing yet
money = money + 250 -- reassign
print(name, money) -- Mike 750Without local, a variable is global: visible to every file of the resource on that side, easy to overwrite by accident and slower to access. Make local a reflex. The one common exception is a config table like Config = {} that is deliberately shared between files.
The types you will meet
| Type | Example | Notes |
|---|---|---|
| nil | nil | “No value”. Unset variables and missing table keys are nil. |
| boolean | true, false | |
| number | 42, 3.5 | Lua 5.4 separates integers (42) and floats (42.0). |
| string | 'hello', "hi" | Immutable text. |
| table | { 1, 2, x = 3 } | The only data structure: arrays, maps, objects. |
| function | function() end | Functions are values you can store and pass. |
| vector3/4 (CfxLua) | vector3(1, 2, 3) | Positions and rotations, with maths built in. |
Operators
| Operator | Meaning | Example |
|---|---|---|
+ - * / | Arithmetic (/ always returns a float) | 10 / 4 → 2.5 |
// | Integer (floor) division | 10 // 4 → 2 |
% | Remainder | 10 % 4 → 2 |
^ | Power | 2 ^ 3 → 8.0 |
== ~= | Equal, not equal | job ~= 'police' |
< > <= >= | Comparison | money >= 100 |
and or not | Logic | if onDuty and not busy then |
.. | Join strings | 'Hi ' .. name |
# | Length of a string or array | #players |
A handy idiom: local label = item.label or 'Unknown' returns the first value that is not nil or false — Lua’s way of setting a default.
if, for and while
if money >= 1000 then
print('rich')
elseif money > 0 then
print('getting by')
else
print('broke')
end-- numeric: from 1 to 5
for i = 1, 5 do
print(i)
end
-- counting down in steps
for i = 10, 0, -2 do print(i) end
-- over an array, in order
local jobs = { 'police', 'ambulance', 'mechanic' }
for index, job in ipairs(jobs) do
print(index, job)
end
-- over any table (order not guaranteed)
local prices = { bread = 5, water = 2 }
for item, price in pairs(prices) do
print(item, price)
end
-- while
local tries = 0
while tries < 3 do
tries = tries + 1
endLua has no continue; use goto continue with a ::continue:: label, or restructure with if. break leaves a loop early.
Functions
local function fullName(first, last)
return first .. ' ' .. last
end
-- Multiple return values are normal in Lua
local function minMax(a, b)
if a < b then return a, b end
return b, a
end
local lo, hi = minMax(9, 3) -- 3, 9
-- Functions are values
local handlers = {
greet = function(who) print('Hello ' .. who) end,
}
handlers.greet('Mike')Multiple returns matter in FiveM: many natives return several values, for example local found, z = GetGroundZFor_3dCoord(x, y, 1000.0, false).
Strings
local plate = ' ab12cd '
plate = plate:upper():gsub('%s+', '') -- 'AB12CD'
print(('%s has $%d'):format('Mike', 250)) -- Mike has $250
print(('%.2f'):format(3.14159)) -- 3.14
print(#'hello') -- 5
print(('police'):sub(1, 3)) -- pol
print(('a,b,c'):find(',')) -- 2 2('...'):format(...) is string.format, the tidy way to build messages. Note that gsub returns two values (the new string and a count) — wrap it in parentheses if you pass it straight to a function.
What FiveM adds: CfxLua
| Feature | Example | Why it matters |
|---|---|---|
| Vectors | vector3(x, y, z), #(a - b) | Fast positions and distances |
| Backtick hashes | `WEAPON_PISTOL` | Hash computed at load time |
| Threads | CreateThread(function() ... end) | Run code in parallel with the game |
| Wait | Wait(1000) | Pause a thread without freezing the game |
| JSON | json.encode(t), json.decode(s) | Saving tables, talking to NUI and HTTP |
| Resource helpers | GetCurrentResourceName() | Know which resource you are |
Threads and Wait are the part that most affects performance; they have their own guide: threads and Wait. Tables — the structure you will use most — are covered in Lua tables for FiveM.
Frequently asked questions
Is Lua hard to learn for FiveM?
No. It is one of the smallest mainstream languages. With the basics in this guide you can read most FiveM scripts; the harder part is learning the game’s natives and networking.
Why should I always use local?
Globals are shared across every file of the resource, easy to overwrite accidentally and slower to access. local keeps variables private and fast.
Do Lua arrays start at 0 or 1?
At 1. ipairs and # both assume arrays start at index 1.
What is the difference between pairs and ipairs?
ipairs walks an array from 1 in order and stops at the first gap. pairs visits every key of any table, in no guaranteed order.
Which Lua version does FiveM use?
Lua 5.4, with FiveM’s CfxLua extensions. Lua 5.3 was deprecated in June 2025.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- Getting startedLua tables for FiveM developersA table can be an array ({ 'a', 'b' }, indexed from 1) or a dictionary ({ price = 5 }), or both. Loop arrays with ipairs and dictionaries with pairs. #t counts only the array part up to the first gap. Tables are passed by reference, so copying requires a copy function. json.encode and json.decode convert tables for NUI, HTTP and databases.
- Getting startedWrite your first FiveM script, step by stepCreate a folder resources/my_first, add an fxmanifest.lua with fx_version 'cerulean', game 'gta5', a client_script and a server_script, then write a client command that loads a model with RequestModel, waits for HasModelLoaded, spawns it with CreateVehicle and seats the player with SetPedIntoVehicle. Add ensure my_first to server.cfg, restart, and type /car adder in game.
- EngineeringThreads and Wait in FiveM: loops that do not eat framesCreateThread(fn) starts a coroutine that runs alongside the game; Wait(ms) pauses it and lets everything else run. Wait(0) resumes on the next frame, so the loop runs every frame (60+ times a second). Use it only while you must draw or read input every frame; otherwise sleep for hundreds of milliseconds, and make loops adaptive — fast when the player is near something, slow when they are not.
- EngineeringThe FiveM Lua errors you will see most, and their fixes“Attempt to index a nil value” means you used . or [] on something that is nil — the object you expected was never set. “Attempt to call a nil value” means the function does not exist. Arithmetic, concatenate and compare errors mean a value you used is nil. Syntax errors (“'end' expected”, “unexpected symbol”) point at a missing keyword or character near the named line.