PiTyUs.Hire me

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.

Updated 12 min readBy PiTyUs · FiveM developer

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

BagServer accessClient accessExample keys
GlobalGlobalState.keyGlobalState.key (read)weather, onlinePolice
PlayerPlayer(src).state.keyLocalPlayer.state.keyjob, isDead, onDuty
EntityEntity(ent).state.keyEntity(ent).state.keyfuel, 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.lualua
-- 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

client.lualua
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 immediatelyRequests that need an answer (callbacks)
Data tied to an entity that moves between playersLarge 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