State bags: concepts and basic usage
A state bag is a shared sticky note the network keeps in sync for you. Half the time you reach for TriggerClientEvent (an event that pushes data to clients), what you really want is one of these. It is a key/value blob stuck to an entity, a player, or the whole server. Set the value once on the server and every client sees it. Hook a handler and you get told the moment it changes.
Build it
Make the resource folder
Inside your server's resources folder, create this folder:
resources/qu_state_bags
Create the files
Create this exact file layout:
resources/qu_state_bags/
fxmanifest.lua
server.lua
client.lua
Write fxmanifest.lua
Open fxmanifest.lua and paste this:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
client_script 'client.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
Open server.lua and paste this:
RegisterCommand('setmood', function(src, args)
local mood = args[1] or 'calm'
Player(src).state:set('mood', mood, true)
print('[qu_state_bags] set mood ' .. mood .. ' on player ' .. src)
end, false)
Open client.lua and paste this:
local myBagName = ('player:%s'):format(GetPlayerServerId(PlayerId()))
AddStateBagChangeHandler('mood', myBagName, function(bagName, key, value)
print('[qu_state_bags] my mood is now ' .. tostring(value))
end)
Start and test it
Open server.cfg and add this line:
ensure qu_state_bags
Save. Then, in the txAdmin Live Console (or the FXServer console window), run:
ensure qu_state_bags
ensure starts the resource if it is not running yet and restarts it if it is, so it is the safe command for a resource you just added.
Join the server so you are a real player, then run this in the in-game chat box:
/setmood focused
Your server slot may be a different number than 1. The point is the same line appears on both sides: the server set it, the client received it.
Read the bag directly with LocalPlayer.state
Sometimes you do not want to react to a change, you just want the current value right now, for example inside a command or a HUD draw loop. LocalPlayer.state is a shortcut for "my own player state bag" that you can read like a plain table, no handler needed. Add this to client.lua:
RegisterCommand('mymood', function()
print('[qu_state_bags] my mood right now is ' .. tostring(LocalPlayer.state.mood))
end, false)
Run ensure qu_state_bags again to pick up the change, then in chat run /setmood happy followed by /mymood. The change handler from Step 5 still fires the moment /setmood runs; /mymood is a separate way to check the same value on demand, whenever you need it, without waiting for anything to change.
Keep reading the full lesson
Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.
- How it works
- If something went wrong
- What you can do now
- Try it yourself
The remainder of State bags: concepts and basic usage is available to FiveM School members.