START HERE·MAKING THINGS HAPPEN·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 · Making things happen

Events: how your code talks

So far your code has run on its own, top to bottom. A real FiveM server is different: lots of separate scripts run at the same time, and the player's game and the server are two separate programs that cannot read each other's memory. So how do they talk? They send each other messages. Those messages are called events, and they are the single most used idea in all of FiveM. By the end of this lesson you will have sent one yourself and watched it land.

You'll learn
What an event is, why events exist, the three you will use most, the safety rule that protects your server, and how to build a tiny working event yourself.
Time
~11 minutes
Difficulty
Beginner
You need
The qu_hello practice resource from earlier lessons, and a test server you can restart. If you skipped "Your first resource", do that first.
BEFORE YOU START

What an event is

An event is a named message your code can send and listen for. That is the whole idea. One piece of code announces "this thing happened" by sending the message, and any other piece of code that cares has said "tell me when that message arrives" and reacts to it.

Two words do all the work. You trigger an event to send it, and you write a handler to listen for it. The handler is just a block of code that runs whenever the matching message arrives, the same way the body of a command runs when you type the command.

Events have names, and the names are plain strings you choose, like qu_hello:greet. The colon in the middle is not special to FiveM. It is a naming habit: resourceName:action. Putting your resource name in front stops a clash the day another resource also wants an event called greet. Two sides simply have to agree on the exact same name, character for character, or the message goes nowhere.

Vocabulary

event
A named message your code can send and listen for. The name is a plain string you choose, like qu_hello:greet.
trigger
To send an event, so that every handler listening for that name runs. You trigger with TriggerEvent, TriggerServerEvent, or TriggerClientEvent.
handler
A block of code that runs when its event arrives. You attach one with AddEventHandler, or with RegisterNetEvent for events that cross the network.
RegisterNetEvent
The function that marks an event name as allowed to arrive from the other side of the network. Required on the receiving side for any event that crosses between client and server.
source
On the server, the id of the player who sent the event. The server reads it to know who asked, instead of trusting the player to say who they are.

Why events exist

Remember the mental model from the client versus server lesson: the player's game (the client) and the server are two separate programs running on two different machines. They do not share variables. The client cannot reach into the server and read a value, and the server cannot reach into the client. On top of that, even on one machine, every resource is its own walled-off sandbox and cannot read another resource's variables either.

So if these separate parts cannot share variables, how do they cooperate at all? They send events. An event is the seam between two parts that otherwise cannot touch. One side announces a fact, the other side reacts, and neither has to reach inside the other. That is why nearly everything in FiveM, buying an item, healing a player, opening a menu, runs on events. It is the only clean way for separate parts to talk.

The three you will use

There are three trigger functions you will reach for constantly. They differ in one thing only: which direction the message travels.

TriggerEvent: same side

TriggerEvent fires an event on the same side you are already on. Client code triggers it, only client handlers hear it. Server code triggers it, only server handlers hear it. It never crosses the network. You listen for it with AddEventHandler.

lua
AddEventHandler('qu_hello:greet', function(name)
    print('[qu_hello] greet handler heard ' .. name)
end)
 
TriggerEvent('qu_hello:greet', 'world')

Read it top to bottom. AddEventHandler says "when an event named qu_hello:greet fires, run this function". The function takes one value, name. Then TriggerEvent fires that exact name and hands it the value 'world', which arrives as name. The handler runs and prints [qu_hello] greet handler heard world. Same side, no network involved.

TriggerServerEvent: client to server

TriggerServerEvent is the client saying "fire this event over there on the server". The message leaves the player's machine and crosses the network to the server. This is how a player's click reaches your server logic.

CLIENT
the player's game
network
SERVER
validate here
✓ trust boundary
A client TriggerServerEvent crosses the network wall once. The server is the only side you can trust, so the check lives there.

The picture above is the shape to memorize. The client triggers, the value crosses the wall, and the server is where you check it. On the server side you must listen with RegisterNetEvent, not AddEventHandler, because the event arrives from across the network:

lua
RegisterNetEvent('qu_bank:deposit', function(amount)
    local src = source
    print('[qu_bank] player ' .. src .. ' wants to deposit ' .. tostring(amount))
end)

RegisterNetEvent marks the name as allowed to arrive from the other side and attaches the handler in one call. The first line, local src = source, grabs the id of the player who sent the event. The server reads who the player is from source. It never asks the player to say who they are.

TriggerClientEvent: server to client

TriggerClientEvent is the reverse trip: the server sending an event back to a client. It mirrors TriggerServerEvent with one extra piece in second place, the target, which says who should receive it.

lua
TriggerClientEvent('qu_bank:balance', src, 1500)

Here the server sends qu_bank:balance to one specific player, the one whose id is in src, and hands them the value 1500. To send to every connected player at once, you pass -1 as the target instead of one id. On the client side you again listen with RegisterNetEvent, because the message arrives from across the network.

Build a tiny event

Time to send a real one. You will register a net event on the server that prints a message, then trigger it from a command on the client. This proves a value crossing from the client to the server, which is the move underneath almost every feature you will ever build.

Add a server handler

The server is listening for one net event.

Open server.lua inside your qu_hello resource and add this:

lua
RegisterNetEvent('qu_hello:ping', function()
    local src = source
    print('[qu_hello] ping from player ' .. src)
end)

RegisterNetEvent opens the gate so this event is allowed to arrive from a client. The handler grabs source into src (always the first line on a server handler) and prints who sent it.

Add a client command that fires it

A typed command sends the event across the wall.

Open client.lua in the same resource and add this:

lua
RegisterCommand('ping', function()
    TriggerServerEvent('qu_hello:ping')
end, false)

RegisterCommand wires the word ping to code, exactly like in earlier lessons. When you type it, TriggerServerEvent fires the matching event name across the network to the server. The false lets you run it from your own game.

Reload the resource

The server loads both new files.

In the txAdmin Live Console, type this and press Enter:

text
restart qu_hello

Fire the command and read the server console

Your event lands and the server prints it.

Join the server, press F8 to open the client console, type ping, and press Enter. Then look at the server console (the txAdmin Live Console), not F8, because the print lives in server code.

The number is your own server id, so it may not be exactly 1, but a player id will appear. You just sent a message from your game, across the network, to the server, and the server told you who it came from. That round trip is the backbone of FiveM.

The safety rule

Here is the rule that protects your server, and it is the most important sentence in this lesson: never trust event data from a client. The server must check it before acting on it.

The reason is simple and a little scary. A normal player triggers your event with sensible values. But a player running a cheat menu can open their console and fire any event you registered with RegisterNetEvent, with any values they invent. Your RegisterNetEvent is effectively a public button anyone on your server can press, with any payload they like.

You will learn the full validation checklist in the paid Tracks. For now, lock in the instinct: data from a client is a request, not a fact. The server is the judge.

Common beginner mistakes

SymptomFix
The client fires the event but the server handler never runs.The receiving side used AddEventHandler instead of RegisterNetEvent. A net event arriving from across the network is rejected unless its name was registered with RegisterNetEvent on the side that receives it. Switch the server handler to RegisterNetEvent.
The server happily gives a player whatever amount they sent.You trusted client data. A player can fire the event with any value. The server must check the type and range, and read identity from source, before acting. Never act on a raw client value.
Nothing happens at all, no print, no error.The event name does not match on both sides. A single typo, a missing colon, or a capital letter difference means the message you trigger matches no handler. Compare the two strings character by character.
A client triggers a server event saying 'give me $1,000,000'. Why must the server not just believe it?

Because the value came from the client, and the client cannot be trusted. A normal player would send a sensible amount, but a player running a cheat menu can fire that same event with any number they type. The server's handler runs with the server's full authority, so whatever it does, it does for real. If the server believes the client and adds the money, the cheater is now a millionaire. The server has to decide for itself: read who the player is from source, check the amount is a number in a sane range, and confirm the player is actually allowed to receive it, before it changes anything. The client makes a request. The server makes the decision.

Try it yourself

What you can do now

  • Explain that an event is a named message you trigger to send and write a handler to listen for.
  • Say why events exist: the client and server cannot share variables, so they send messages instead.
  • Pick the right trigger for the direction: TriggerEvent for the same side, TriggerServerEvent for client to server, TriggerClientEvent for server to client.
  • Use RegisterNetEvent (not AddEventHandler) on the receiving side of any event that crosses the network.
  • State the safety rule: never trust client data; the server must validate it and read identity from source.

You can now make separate parts of a server talk to each other, which is most of what real features are made of. The remaining piece is the simplest way to set an event off on purpose: a command you type. Next up: "Commands: trigger code on demand".