FiveM Lua Scripting — Write Your First Script
You want to make your own FiveM scripts instead of relying on what other people build. Good. That’s how I started too, and honestly it’s not as hard as it looks from the outside.
This guide assumes you know basically nothing about Lua or FiveM scripting. By the end, you’ll have a working script that does something visible in-game, and you’ll understand enough to start modifying other people’s scripts or building your own.
What You Need
- A FiveM server — even a local one is fine for testing. See my server setup guide if you don’t have one.
- A text editor — VS Code is the standard. Install the “FiveM Lua” or “sumneko Lua” extension for syntax highlighting and autocomplete.
- Patience — your first script will probably break. That’s normal. That’s how you learn.
Lua in 5 Minutes
Lua is the scripting language FiveM uses. If you’ve used JavaScript or Python, Lua will feel familiar. If you haven’t programmed before, don’t worry — Lua is one of the simpler languages to pick up.
Here’s the stuff you need to know right now:
-- Variables
local name = "YBN" -- text (string)
local health = 200 -- number
local isAlive = true -- boolean (true/false)
-- If statements
if health > 100 then
print("Healthy")
elseif health > 0 then
print("Hurt")
else
print("Dead")
end
-- Loops
for i = 1, 10 do
print(i) -- prints 1 through 10
end
-- Tables (Lua's version of arrays/objects)
local fruits = {"apple", "banana", "orange"}
print(fruits[1]) -- "apple" (Lua starts at 1, not 0)
local player = {
name = "John",
job = "police",
money = 5000
}
print(player.name) -- "John"
-- Functions
local function greet(playerName)
return "Hello, " .. playerName -- .. concatenates strings
end
print(greet("John")) -- "Hello, John"
That’s genuinely enough Lua to start writing FiveM scripts. You’ll pick up the rest as you go.
How FiveM Scripts Work
Every FiveM script (called a “resource”) is a folder with a specific structure:
my_script/
├── fxmanifest.lua -- tells FiveM what this resource is
├── client.lua -- runs on each player's game
└── server.lua -- runs on the server
fxmanifest.lua
This is the manifest file — it tells FiveM what your resource contains:
fx_version 'cerulean'
game 'gta5'
name 'my_script'
description 'My first FiveM script'
author 'YBN Scripts'
client_script 'client.lua'
server_script 'server.lua'
That’s it. Not complicated.
Client vs Server
This is the most important concept to understand:
Client scripts run on each player’s computer. They handle:
- Drawing things on screen (markers, text, UI)
- Detecting player input (key presses)
- Reading the player’s position, vehicle, etc.
- Anything visual or local to one player
Server scripts run on the server. They handle:
- Database queries
- Money transactions
- Inventory changes
- Anything that needs to be authoritative (anti-cheat, validation)
- Communication between players
The rule: Never trust the client. If a player could benefit from lying about a value (money, health, items), that logic needs to be on the server. The client can request things, but the server decides.
Your First Script: A Teleport Command
Let’s build something that actually works. We’ll make a /teleport command that moves you to a set of coordinates.
Create a new folder in your resources/ directory called my_first_script.
fxmanifest.lua:
fx_version 'cerulean'
game 'gta5'
name 'my_first_script'
description 'Teleport command tutorial'
author 'Me'
server_script 'server.lua'
server.lua:
RegisterCommand('teleport', function(source, args, rawCommand)
-- source = the player's server ID (0 if typed in console)
if source == 0 then
print("This command can only be used in-game")
return
end
-- Get the player's ped (character model)
local ped = GetPlayerPed(source)
-- Teleport to Legion Square
SetEntityCoords(ped, 195.0, -933.0, 30.0)
-- Let the player know
TriggerClientEvent('chat:addMessage', source, {
args = {"System", "Teleported to Legion Square!"}
})
end, false) -- false = anyone can use it
Now ensure my_first_script in your server.cfg, restart, and type /teleport in chat. You’ll teleport to Legion Square.
Congratulations — you just wrote a FiveM script.
Making It Better: Multiple Locations
That was cool but kind of useless. Let’s add multiple teleport locations:
local locations = {
["legion"] = {x = 195.0, y = -933.0, z = 30.0, label = "Legion Square"},
["airport"] = {x = -1037.0, y = -2737.0, z = 20.0, label = "Airport"},
["sandy"] = {x = 1848.0, y = 3694.0, z = 34.0, label = "Sandy Shores"},
["paleto"] = {x = -170.0, y = 6336.0, z = 31.0, label = "Paleto Bay"},
}
RegisterCommand('tp', function(source, args, rawCommand)
if source == 0 then return end
local locationName = args[1]
if not locationName then
TriggerClientEvent('chat:addMessage', source, {
args = {"System", "Usage: /tp [legion|airport|sandy|paleto]"}
})
return
end
local loc = locations[string.lower(locationName)]
if not loc then
TriggerClientEvent('chat:addMessage', source, {
args = {"System", "Unknown location: " .. locationName}
})
return
end
SetEntityCoords(GetPlayerPed(source), loc.x, loc.y, loc.z)
TriggerClientEvent('chat:addMessage', source, {
args = {"System", "Teleported to " .. loc.label}
})
end, false)
Now you can type /tp airport or /tp sandy. See how easy it is to extend?
Understanding Natives
“Natives” are the functions GTA V exposes to FiveM. Things like GetEntityCoords, SetEntityHealth, CreateVehicle — these are all natives. There are thousands of them.
The FiveM documentation at docs.fivem.net/natives lists them all. It can be overwhelming at first, but you only need a handful to do most things:
Common client-side natives:
PlayerPedId()— get your characterGetEntityCoords(ped)— get positionGetEntityHeading(ped)— get direction you’re facingIsPedInAnyVehicle(ped)— check if in a vehicleGetVehiclePedIsIn(ped)— get the vehicleDrawMarker(...)— draw a 3D marker in the world
Common server-side natives:
GetPlayerPed(source)— get a player’s character on the serverGetPlayerName(source)— get their nameSetEntityCoords(entity, x, y, z)— move an entityGetNumPlayerIdentifiers(source)— get player identifiers (Steam, license, Discord)
Events: Client-Server Communication
Client and server scripts talk to each other through events. This is how you send data between them:
Server → Client:
-- Server sends a message to a specific player
TriggerClientEvent('myScript:notify', playerId, "Hello from the server!")
-- Server sends to ALL players
TriggerClientEvent('myScript:notify', -1, "Server announcement!")
-- Client listens for it
RegisterNetEvent('myScript:notify')
AddEventHandler('myScript:notify', function(message)
-- do something with the message
print(message)
end)
Client → Server:
-- Client sends to server
TriggerServerEvent('myScript:requestMoney', 500)
-- Server listens
RegisterNetEvent('myScript:requestMoney')
AddEventHandler('myScript:requestMoney', function(amount)
local src = source -- always capture source in a local variable
-- validate, check permissions, then give money
-- NEVER trust the amount the client sends without validating it
end)
Security note: Any player with a mod menu can trigger any client event. Never do something like TriggerServerEvent('giveMoney', 999999) and have the server blindly trust it. Always validate on the server side.
Threads and Loops
FiveM uses threads to run continuous logic. This is how you check for things happening in the game world:
-- Client-side: draw a marker every frame
Citizen.CreateThread(function()
while true do
local sleep = 500 -- check every 500ms
local playerCoords = GetEntityCoords(PlayerPedId())
local markerCoords = vector3(195.0, -933.0, 29.0)
local distance = #(playerCoords - markerCoords)
if distance < 20.0 then
sleep = 0 -- close enough, draw every frame
DrawMarker(1, markerCoords.x, markerCoords.y, markerCoords.z,
0,0,0, 0,0,0, 1.0,1.0,1.0, 255,0,0,100, false,false,2,false)
if distance < 1.5 then
-- player is right on top of the marker
-- show interaction prompt, trigger something, etc.
end
end
Wait(sleep)
end
end)
Notice the sleep variable pattern — this is critical for performance. Don’t run expensive checks every frame when you only need them occasionally. I go deep on this in my performance guide.
Where to Go From Here
You now know enough to:
- Read and understand most FiveM scripts
- Modify configs and simple logic in existing scripts
- Write basic scripts of your own
Next steps:
- Read other people’s code. Download free scripts from the CFX forums and read through them. You’ll learn patterns and tricks.
- Build something small. A custom command, a simple marker interaction, a notification system. Keep it small until you’re comfortable.
- Learn your framework’s API. If you’re on ESX, learn how
ESX.GetPlayerFromId(),xPlayer.addMoney(), etc. work. The framework docs are your friend. - Join developer communities. The Cfx.re Discord has a scripting channel. Our Discord has devs who can help too.
Don’t try to build a full MLO interior or a complete job script as your second project. Scale up gradually. You’ll get there faster than you think.
Related Guides
- How to Install FiveM Scripts — the basics of adding scripts to your server
- ESX vs QBCore vs Qbox — pick the right framework before you start scripting for it
- Script Performance Guide — write scripts that don’t lag
- Our Free Scripts — open-source examples you can study and learn from