Engineering
State bags in FiveM: synced data without events
How FiveM state bags work: GlobalState, Player(source).state and Entity(entity).state, replication and who can write, AddStateBagChangeHandler, and when state bags beat events for syncing data.
Overview
Many FiveM scripts sync data the hard way: a server event every time something changes, a client event to ask for the current value, and a table on each side that drifts out of date. State bags replace all of that with key–value storage attached to the server, to a player or to an entity, which FiveM keeps in sync for you. Once you use them, a lot of event plumbing disappears.
What a state bag is
| Bag | Server access | Client access | Example keys |
|---|---|---|---|
| Global | GlobalState.key | GlobalState.key (read) | weather, onlinePolice |
| Player | Player(src).state.key | LocalPlayer.state.key | job, isDead, onDuty |
| Entity | Entity(ent).state.key | Entity(ent).state.key | fuel, locked, owner |
Entity state bags require OneSync, which modern servers run by default. On the client, entity state is addressed through the entity handle; on the server through the server-side handle. Across the network the entity is identified by its network ID — see entity ownership and network IDs.
Writing values
-- Server-wide value, replicated to every client
GlobalState.onlinePolice = 4
-- A player's value
Player(source).state:set('onDuty', true, true) -- key, value, replicated
-- A vehicle's value
local veh = GetVehiclePedIsIn(GetPlayerPed(source), false)
Entity(veh).state:set('fuel', 62.5, true)The third argument of :set is replicated. With true the value is sent to clients; with false it stays on the side that set it. Assigning with state.key = value on the server replicates by default.
Reading and reacting
print(GlobalState.onlinePolice)
print(LocalPlayer.state.onDuty)
-- React whenever any player's 'onDuty' changes
AddStateBagChangeHandler('onDuty', nil, function(bagName, key, value, _reserved, replicated)
local player = GetPlayerFromStateBagName(bagName)
if player == 0 then return end
print(('player %d on duty: %s'):format(GetPlayerServerId(player), tostring(value)))
end)
-- React to fuel on any entity
AddStateBagChangeHandler('fuel', nil, function(bagName, _, value)
local entity = GetEntityFromStateBagName(bagName)
if entity == 0 then return end
-- update a fuel gauge if this is our vehicle
end)The second argument filters by bag name (nil means all bags). GetPlayerFromStateBagName and GetEntityFromStateBagName convert the bag name back into something you can use. A handler can fire before the entity exists locally, which is why the example checks for 0.
State bags or events?
| Use state bags for… | Use events for… |
|---|---|
| Values that describe current state (duty, fuel, locked) | Things that happen once (a notification, an explosion) |
| Data late joiners must see immediately | Requests that need an answer (callbacks) |
| Data tied to an entity that moves between players | Large payloads sent rarely |
The big win is late joiners: a player who connects after a value was set sees it immediately, with no “ask the server for the current state” event. Events are covered in FiveM events explained.
Performance and limits
- Every replicated change is network traffic to every client that should see it. Do not write a value every frame.
- Keep values small: numbers, short strings and small tables.
- Round noisy values (fuel to one decimal place) and only set when they actually change.
- Clean up keys you no longer need by setting them to
nil.
Frequently asked questions
What are state bags in FiveM?
Key–value data attached to the server (GlobalState), a player or an entity, which FiveM replicates to clients automatically.
Do state bags need OneSync?
Entity state bags do. Modern servers run OneSync by default.
Can clients change state bags?
Clients can write their own player state and state on entities they own, unless the server blocks it — and those values can be forged, so never trust them for anything important.
How do I listen for state bag changes?
AddStateBagChangeHandler(key, bagFilter, handler). The handler receives the bag name, key and new value.
Are state bags better than events?
For ongoing state that late joiners need, yes. For one-off actions and request/response, events or callbacks remain the right tool.
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
- EngineeringEvents in FiveM: how resources and players talkHandle events with AddEventHandler(name, fn); add RegisterNetEvent(name) (or use RegisterNetEvent(name, fn)) to allow the event to arrive over the network. TriggerEvent fires locally on the same side, TriggerServerEvent sends from a client to the server, and TriggerClientEvent(name, target, ...) sends from the server to one player (target = player ID) or everyone (-1). On the server, source is the sending player.
- EngineeringEntity ownership, handles and network IDs in FiveMEntity handles are local to each client and to the server; never send them over events. Convert to a network ID with NetworkGetNetworkIdFromEntity, send the ID, and convert back with NetworkGetEntityFromNetworkId (or NetToVeh/NetToPed/NetToObj on the client). Each networked entity has an owner that simulates it; other clients must request control before changing it, and ownership migrates when the owner leaves.
- EngineeringWriting server events that cannot be abusedAny client can call any event you registered with RegisterNetEvent, with any arguments. In each handler: copy source into a local, check argument types and ranges, re-derive everything from server state (prices, amounts, rewards), verify the player can do this now (distance, job, item, cooldown), and log refusals. Events only other server scripts should use are registered with AddEventHandler alone, so clients cannot trigger them.
- Getting startedClient-side vs server-side in FiveMClient scripts run on each player’s PC and handle what that player sees and does: drawing, input, markers, animations and local effects. Server scripts run once, on your server, and own everything that has value or must be trusted: money, items, jobs, the database and permissions. The client asks; the server decides. Never trust data sent by a client without checking it.