Skip to content

Change listeners โ€‹

How OnChanged callbacks work on the server and client, and what the third argument contains.

Signatures โ€‹

SideAPI
SERVER APIOnChanged(player, pathOrKey, callback)
CLIENT APIOnChanged(pathOrKey, callback)

Types: ChangeInfo ยท ChangeCallback.

Third argument: path array vs ChangeInfo โ€‹

SituationThird argument (info / changeInfo)
Direct Set / SetValues on the listened pathOften a path key array, e.g. { "Coins" }
Table insert/remove, or child path changed while listening on a parentChangeInfo table with action, path, optional index
Listening on "." (every change)Always a table with action and path

ChangeInfo actions include "Set", "SetValues", "TableInsert", and "TableRemove".

lua
-- Client: table insert
PlayerState.OnChanged("Inventory", function(newValue, oldValue, info)
    if type(info) == "table" and info.action == "TableInsert" then
        print("Added at index", info.index)
    end
end)

Server-only paths โ€‹

OnChanged is tied to the player Replica. Writes under ServerOnlyRoots (e.g. Server.*) update profile.Data only and do not fire OnChanged. After server-only writes, use your own flow or read with GetPath / GetAll.

Client practices โ€‹

  • Prefer specific paths ("Coins", "Plot.Likes") over "." except when debugging.
  • Disconnect when UI is destroyed or the player leaves.
  • Do not mutate tables returned from Get / GetPath โ€” see Best practices.

Server practices โ€‹

  • Disconnect per-player connections in PlayerRemoving.
  • Same narrow-path guidance as the client.

API reference โ€‹

PlayerState - High-Performance Roblox Data Management