Skip to main content
TRACK B·LUA FUNDAMENTALS·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this lesson plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.

Your first line of Lua

Every coder starts the same way: one line, one message, one visible result. In this lesson that result is a line you control, printed in your FiveM server console on demand. By the end you will understand a string, the print function, and how RegisterCommand turns a typed word into running code.

You'll build
A tiny resource that prints a message to the server console when you run a command.
Time
~12 minutes
You need
A local FiveM practice setup you can restart, and a text editor (VS Code is free). If you do not have that yet, do the 7-day browser starter first, then come back here.
You'll learn
Lua strings, the print function, what a command handler is, and how to read the console as your feedback loop
BEFORE YOU START

Build it

Make the resource folder

The server has one folder for this lesson.

Inside your server's resources folder, create this folder:

text
resources/qu_first_line_of_lua

Create the files

Every file named in the manifest exists.

Create this exact file layout:

text
resources/qu_first_line_of_lua/
fxmanifest.lua
server.lua

Write fxmanifest.lua

FiveM knows which files to load.

Open fxmanifest.lua and paste this:

lua
fx_version 'cerulean'
game 'gta5'
 
server_script 'server.lua'

Older tutorials add a lua54 'yes' line here. As of June 2025 that setting is deprecated and ignored: Lua 5.4 is now the only Lua runtime, so you leave it out.

Write the lesson code

The topic is now represented by runnable code.

Open server.lua and paste this:

lua
RegisterCommand('first_line_of_lua', function()
print('[qu_first_line_of_lua] proof line')
end, true)

Start and test it

The expected proof appears in the correct console.

Open server.cfg and add this line:

text
ensure qu_first_line_of_lua

Save the file. Now open the txAdmin Live Console (the text box at the bottom of the txAdmin web panel where you type server commands) and type this, then press Enter:

text
restart qu_first_line_of_lua

Still in that same txAdmin Live Console, type the command itself and press Enter:

text
first_line_of_lua

How it works

You ran it and the line appeared. Now take the code apart so the next time you write Lua you are deciding, not copying. There are really only two lines that matter, and one piece of punctuation that trips up everyone at the start.

The string you printed

Look at the text inside the parentheses on the print line:

lua
'[qu_first_line_of_lua] proof line'

That is a string: a run of text that Lua treats as a single value. The single quotes are not part of the text. They are fences that mark where the text starts and ends. Lua also accepts double quotes, so "proof line" means the same thing as 'proof line'. Pick one style and stay consistent. The [qu_first_line_of_lua] part at the front is just text you typed on purpose. It is a tag, so that when twenty resources are all printing at once, you can scan the console and find your own lines. That habit pays off the moment your server has more than one script running.

The print function

lua
print('[qu_first_line_of_lua] proof line')

print is a function: a named action you can call. You call it by writing its name, then a pair of parentheses, and inside the parentheses you put what you want it to act on. Here you hand it one string. The value you hand a function is called an argument. So in plain words this line says, take this string and write it to the console. That is the entire job of print. It does not pop up on a player's screen and it does not appear in chat. It writes to the server console. If you run your server through txAdmin, that output shows up in the txAdmin Live Console (it is also the same text you would see in the raw FXServer console window).

The mental model to lock in: print is your eyes inside running code. Code that is working silently tells you nothing. A well-placed print is how you ask the program what it is actually doing, instead of guessing. Every debugging session you will ever run starts with the same move, dropping a print somewhere to see whether the code even reached that point.

The command that runs it

lua
RegisterCommand('first_line_of_lua', function()
    print('[qu_first_line_of_lua] proof line')
end, true)

This is the line that decides when your print runs. RegisterCommand is a FiveM native: a function the platform gives you for free. It wires a typed command to a block of code. Read it as three arguments separated by commas:

  • 'first_line_of_lua' is the command name, a string. This is the word you type in the console to fire it. It does not have to match the resource name. It just happens to here.
  • function() ... end is the code to run when the command fires. Everything between function() and end is the body. Right now the body is one print, but it could be a hundred lines. This is called the handler or callback: code you hand to FiveM and say, hold onto this and run it later, when the command is typed.
  • true is the restricted flag. With true, the command requires an ace permission, which is why it runs cleanly from the server console. The server console has full rights. Set it to false and any connected player could run the command from their own chat box. For a server-side proof line you do not want players triggering, true is the safe default. Security rule to carry forward: any command that changes game state or trusts player input (heal, give item, set money) must stay restricted (true) and gated behind an ace permission. A command left at false lets every connected player fire that code from chat, and unauthenticated player-triggered server actions are the single most common way FiveM servers get exploited.

The end keyword closes the function body. Lua does not use curly braces to mark where a block stops the way JavaScript or C# do. It uses the word end. A missing end is the single most common Lua error a beginner hits, so train your eye to pair every function with its end now.

Here is the flow in order. The server starts the resource and reads server.lua top to bottom. It hits RegisterCommand and registers the command, but it does not run the print yet. The print is sitting inside the handler, waiting. Later, when you type first_line_of_lua in the console, FiveM finds the matching command, runs the handler, and the print fires. Registering and running are two separate moments. That gap is the whole idea behind event-driven code, and almost everything you build in FiveM works this way: you register handlers up front, and they run later when something happens.

Why does nothing print the instant the resource restarts, only when you type the command?

Because the print lives inside the command handler, not at the top level of the file. On restart, FiveM runs server.lua once and reaches RegisterCommand, which only registers the command and stores the handler for later. The handler body, including the print, runs only when the command name is actually typed. If you wanted a line to appear the moment the resource starts, you would put the print outside the handler, at the top level of the file, the way the Anatomy of a resource lesson does it.

When you reach for this pattern

A command plus a print is the smallest useful tool in FiveM, and you will use it constantly even after you are far past beginner. Three real cases:

  • A manual trigger while building. You are writing a feature and you want to fire one part of it on demand without waiting for the real game condition. A command is the fastest way to poke your code by hand.
  • A debug probe. You suspect a value is wrong. Register a throwaway command that prints the value, type it whenever you want a reading, then delete it when you are done.
  • An admin action. Heal, give an item, teleport. These are commands at heart, gated with the restricted flag so only staff can run them. You just built the skeleton every one of those shares.

If something went wrong

Stuck? Reset to a known-good state: re-paste the exact fxmanifest.lua and server.lua from Build it (overwriting whatever you have), then in the txAdmin Live Console run refresh; restart qu_first_line_of_lua and try the command again. refresh makes the server re-read your manifest; restart reloads the resource.

SymptomFix
attempt to call a nil value (global 'RegisterCommand')The native name is misspelled. It is RegisterCommand with a capital R and a capital C. Lua is case sensitive, so registercommand will not resolve.
')' expected near 'end'A quote, comma, or paren is missing on the RegisterCommand line. Count the parentheses and make sure both the print string and the command name are wrapped in matching quotes.
'end' expected (near <eof>)The function has no closing end, or the final end and the closing paren of RegisterCommand got deleted. The block must finish with end, then the closing paren and the true flag.
The command does nothing when a player runs it in chatThe restricted flag is true, so only the server console (and aces) may run it. Run first_line_of_lua from the txAdmin Live Console, not the in-game chat box (press T).

What you can do now

  • Read a Lua string and explain why the quotes are fences, not text.
  • Use print to write a tagged line to the server console as a feedback probe.
  • Explain that RegisterCommand registers a handler now and runs it later when the command is typed.
  • Say what each of the three RegisterCommand arguments does, including why the restricted flag is true.
  • Pair every function with its end and spot the error when one is missing.

Run it live

No server needed for this part. Edit the Lua below and press RUN to execute it in your browser on real Lua 5.4. Change the message, add a second print, and watch the output. The sandbox has no FiveM around it, so RegisterCommand and other natives are not available here. Plain Lua like print and strings run exactly as they would on your server.

sandbox.luaLUA 5.4
OUTPUT
Press RUN to execute.

Try it yourself