START HERE·LUA FROM ZERO·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this whole lesson - every step, code block, and the exact console errors - plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.
Start Here · Lua from zero

Tables: Lua's one flexible container

A table is the one flexible container Lua gives you. It can hold a plain list of things, like three fruit names in a row. It can also hold labelled data, like a player who has a name and a level. The reason this matters for FiveM is simple: almost everything you touch in FiveM is a table. A player is a table. A config file is a table. The arguments handed to your commands arrive in a table. Learn this one structure well and a huge amount of real code stops looking like noise and starts looking like data you can read.

You'll learn
What a table is, how Lua uses one structure for both lists and labelled data, why arrays start at 1, and how to loop over a table with ipairs and pairs.
Time
~12 minutes
Difficulty
Beginner
You need
Nothing to install. You will run plain Lua right here in the browser.

Watch

RED: a recap of the Lua fundamentals you just learned.
BEFORE YOU START

What a table is

A table is a container that holds many values under one variable name. Up to now each variable you made held a single thing: one number, one string, one true-or-false. A table is different. You make one variable, and inside it you can store as many values as you like.

Think of a variable like a single labelled box on a shelf. A table is a box that has many smaller compartments inside it. You still grab the one box by its name, then reach into whichever compartment you want.

You write a table with curly braces. Everything you put between the { and the } lives inside that one table:

lua
local fruits = { "apple", "banana", "cherry" }

That single line creates one table and stores it in a variable called fruits. The table holds three values.

Vocabulary

table
Lua's container type. One value that can hold many other values inside it. Written with curly braces { }.
array
A table used as a plain ordered list, where each value sits at a numbered position. Our fruits table is an array.
key
The label you use to reach a value inside a table. For an array the keys are numbers (1, 2, 3). For labelled data the keys are words (name, level).
value
The actual thing stored at a key. In the fruits table, "apple" is a value.
index
Another word for a number used as a key. fruits[1] reads the value at index 1.

Tables as lists (arrays)

When a table holds a plain ordered list, we call it an array. Each value sits at a numbered position, and you reach a value by writing the table name followed by its position in square brackets.

Here is the single most important fact in this entire lesson, and the one that catches more beginners than anything else:

Look at how you read a value back out:

lua
local fruits = { "apple", "banana" }
 
print(fruits[1])    -- prints apple, the first item
print(fruits[2])    -- prints banana, the second item

The square brackets are how you say "reach into this table and give me the value at this position." fruits[1] means "the value at position 1 of fruits."

Why does Lua start at 1 when so many other languages start at 0? It is a design choice from the people who created Lua. They wanted it to read the way humans count, where the first thing is the first, not the zeroth. You do not have to agree with the choice. You just have to remember it, because Lua will never bend on this. Position 1 is the first item, always.

See it run

Press RUN to execute this on real Lua 5.4 right in your browser. No FiveM and no server needed, just plain Lua.

sandbox.luaLUA 5.4
OUTPUT
Press RUN to execute.

The first line printed apple because that is the value at index 1, the front of the list.

The second line printed 3. That came from #fruits. The # symbol in front of a table gives you its length, meaning how many values are in the list. Our table holds three fruits, so #fruits is 3. You will use this constantly to ask "how many items are in here," for example to check whether a list is empty before you loop over it.

Try changing a value, adding a fourth fruit, or printing fruits[2] and run it again. Watching the output change is how the rule sinks in.

Tables as labelled data (dictionaries)

A table does not have to be a numbered list. It can also store values under word labels instead of numbers. When you do that, people often call it a dictionary, because like a real dictionary you look something up by its name rather than by its position.

Here the keys are words, not numbers:

lua
local player = {
    name = "Quasar",
    level = 5,
}

This one table holds two pieces of data about a player: a name and a level. Now you reach a value by its label, and there are two ways to write it that do exactly the same thing:

lua
print(player.name)        -- prints Quasar, dot notation
print(player["name"])     -- prints Quasar, bracket notation, same result

The dot form, player.name, is the one you will see most often because it is shorter and cleaner. The bracket form, player["name"], is handy when the key is stored in another variable, but for everyday code reach for the dot.

This is exactly why we said almost everything in FiveM is a table. A player in a real script is just a bigger version of this: one table holding a name, a level, an amount of cash, a job, and so on, each under its own label.

sandbox.luaLUA 5.4
OUTPUT
Press RUN to execute.

Looping over a table

Reading one value at a time is fine when you know exactly what you want. But often you have a whole table and you want to do something with every value in it. That is a loop: code that runs once for each item in the table. Lua gives you two loop helpers, and which one you use depends on whether your table is a list or labelled data.

ipairs for arrays in order

For an array, use ipairs. It walks the table in order, from index 1 upward, and on each pass it hands you two things: the index and the value at that index.

lua
local fruits = { "apple", "banana", "cherry" }
 
for i, fruit in ipairs(fruits) do
    print(i, fruit)
end

Read the first line as "for each index i and value fruit in fruits, do the following." The word do opens the body of the loop, and end closes it, the same end you met with functions. The body runs once per item.

sandbox.luaLUA 5.4
OUTPUT
Press RUN to execute.

Notice the loop counted from 1, in order, just like the array itself. That is what ipairs is for: ordered lists.

pairs for key/value data

For labelled data, use pairs. It walks every key in the table and hands you the key and its value. The one catch is that pairs does not promise any particular order, because labelled data has no natural front-to-back the way a list does.

lua
local player = { name = "Quasar", level = 5 }
 
for key, value in pairs(player) do
    print(key, value)
end

This prints both pairs, name with Quasar and level with 5, but do not count on which one comes first. When order matters, you have a list, so reach for an array and ipairs. When you just need to visit every labelled field, pairs is your tool.

Tables inside tables

A value in a table can itself be another table. That is how real FiveM data is built: a server config is a table whose shop key holds another table, whose items key holds an array. You read your way in one step at a time, config.shop.items[1], which means "go into config, then into shop, then take the first item." You do not need this yet. Just know that the moment data gets complex, it is still only tables nested inside tables. Nothing new to learn, only deeper.

Common beginner mistakes

SymptomFix
the first item is nil / off by oneLua tables start at index 1, not 0. Use myTable[1] for the first item. If everything you read is shifted by one slot, you almost certainly counted from 0 out of habit.
attempt to index a nil value (field '...')You read a key that does not exist, or a nested table that was never created. Check the key name is spelled exactly right, and that the table you are reaching into actually exists first.
What index is the first item of a Lua array?

It is index 1. Lua arrays start counting at 1, not 0, so the first item is myTable[1]. There is no myTable[0] holding your first value. This is the difference that trips up nearly every beginner, so when a list looks "off by one," check this first.

Try it yourself

What you can do now

You can read and write the core of Lua, and you understand the world it runs in. That is the part most beginners never get past, and it is behind you now.

  • You understand what FiveM is and how a server is put together from resources.
  • You know the split between the client (each player's game) and the server (the shared brain everyone connects to).
  • You can read and write basic Lua: variables, strings, numbers, true-or-false values, conditions, and functions you can reuse.
  • You can build and read tables, both as ordered lists and as labelled data, and loop over them with ipairs and pairs.
  • You know the trap that catches everyone: Lua arrays start at 1.

You did not just watch tutorials. You learned the ideas underneath them. That is the whole difference between someone who copies code and someone who builds.

So far everything has run inside your browser. Next you leave the sandbox: you install FiveM, set up a real editor, and learn where your code lives, so the Lua you just learned can run on an actual server.