Skip to main content

TRGettingStarted

TableReplicator replicates TableManager instances from the server to clients. On the server you create a ServerReplicator, decide who can see it, and mutate its .Manager. Every write is mirrored automatically to a matching ClientReplicator on each targeted client.

This guide walks through the minimal end-to-end setup. For deeper topics see the other guides linked at the bottom of the page.

1. Require the module

The module exposes two entry points — one per realm. Require the one for the side you're on:

-- Server scripts
local ServerReplicator = require(Packages.TableReplicator).Server

-- Client scripts
local ClientReplicator = require(Packages.TableReplicator).Client

2. Create a replicator on the server

ServerReplicator.new takes a single config table. The two fields you'll always set are Data (what to replicate) and Targets (who receives it). Mutate the replicator's .Manager and the change is queued and sent at the end of the frame.

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	local replicator = ServerReplicator.new({
		Namespace = "PlayerData",      -- how clients discover it
		Data = { Coins = 0, Level = 1 }, -- raw table, or an existing TableManager
		Targets = player,              -- a Player, a { Player } list, or "all"
		Tags = { UserId = player.UserId }, -- optional metadata for filtering
	})

	replicator.Manager:Set("Coins", 100) -- replicated automatically

	player.Destroying:Connect(function()
		replicator:Destroy()
	end)
end)

3. Read the data on the client

Clients never create replicators — the server drives their whole lifetime. Use ForEach to run a callback for every matching replicator (existing and future), then call RequestData() once to pull the initial snapshot.

-- Register listeners BEFORE RequestData so they catch the initial snapshot.
ClientReplicator.ForEach("PlayerData", function(replicator)
	replicator.Manager:Observe("Coins", function(coins)
		print("Coins:", coins)
	end)
end)

ClientReplicator.RequestData():andThen(function()
	print("Initial snapshot applied")
end)
Call RequestData() once

Clients must call ClientReplicator.RequestData() at least once to receive the initial snapshot. Otherwise replication will never begin. Subsequent calls are safe no-ops.


The config table at a glance

Field Purpose
Data A raw table (auto-wrapped in a TableManager) or an existing TableManager. Defaults to {}.
Targets Who a top-level replicator sends to: a Player, a { Player } list, or "all". Pass {} to start with none.
Parent A parent ServerReplicator, for child replicators. Mutually exclusive with Targets.
Namespace Optional string (or ReplicationToken) used for discovery. Omit for anonymous replicators.
Tags Optional { [string]: any } metadata for filtering in ForEach/GetAll/GetFirst.
Coalesced Send only the latest op per key each frame, dropping intermediate writes.
ImmediateFlush Send each op immediately instead of batching per frame.
Client Declares custom remotes (signals/functions). See TR Custom Remotes.

Things to be aware of

Exactly one of Targets or Parent

A top-level replicator must be given Targets (pass {} for none). A child replicator must be given Parent and inherits its ancestor's targets. Passing both, or neither, throws.

  • Always destroy when done. Call replicator:Destroy() when a replicator is no longer needed. It also listens to its manager's OnDestroy, so destroying the manager tears the replicator down too.
  • Client code never creates or destroys replicators. Lifetime is server-driven; calling Destroy on a ClientReplicator errors.
  • Call RequestData() once at startup. Subsequent calls are safe no-ops.
  • Setting up OnNew listeners before RequestData(). Listeners added after it resolves may miss replicators that were in the initial snapshot since. OnNew only fires for replicators that arrive after the listener is registered.

See also

Show raw api
{
    "functions": [],
    "properties": [],
    "types": [],
    "name": "TR Getting Started",
    "desc": "TableReplicator replicates [TableManager](/api/TableManager) instances from the\nserver to clients. On the server you create a [ServerReplicator](/api/ServerReplicator),\ndecide who can see it, and mutate its `.Manager`. Every write is mirrored\nautomatically to a matching [ClientReplicator](/api/ClientReplicator) on each\ntargeted client.\n\nThis guide walks through the minimal end-to-end setup. For deeper topics see the\nother guides linked at the bottom of the page.\n\n### 1. Require the module\n\nThe module exposes two entry points — one per realm. Require the one for the side\nyou're on:\n\n```lua\n-- Server scripts\nlocal ServerReplicator = require(Packages.TableReplicator).Server\n\n-- Client scripts\nlocal ClientReplicator = require(Packages.TableReplicator).Client\n```\n\n### 2. Create a replicator on the server\n\n`ServerReplicator.new` takes a single config table. The two fields you'll always\nset are `Data` (what to replicate) and `Targets` (who receives it). Mutate the\nreplicator's `.Manager` and the change is queued and sent at the end of the frame.\n\n```lua\nlocal Players = game:GetService(\"Players\")\n\nPlayers.PlayerAdded:Connect(function(player)\n\tlocal replicator = ServerReplicator.new({\n\t\tNamespace = \"PlayerData\",      -- how clients discover it\n\t\tData = { Coins = 0, Level = 1 }, -- raw table, or an existing TableManager\n\t\tTargets = player,              -- a Player, a { Player } list, or \"all\"\n\t\tTags = { UserId = player.UserId }, -- optional metadata for filtering\n\t})\n\n\treplicator.Manager:Set(\"Coins\", 100) -- replicated automatically\n\n\tplayer.Destroying:Connect(function()\n\t\treplicator:Destroy()\n\tend)\nend)\n```\n\n### 3. Read the data on the client\n\nClients never create replicators — the server drives their whole lifetime. Use\n`ForEach` to run a callback for every matching replicator (existing **and**\nfuture), then call `RequestData()` once to pull the initial snapshot.\n\n```lua\n-- Register listeners BEFORE RequestData so they catch the initial snapshot.\nClientReplicator.ForEach(\"PlayerData\", function(replicator)\n\treplicator.Manager:Observe(\"Coins\", function(coins)\n\t\tprint(\"Coins:\", coins)\n\tend)\nend)\n\nClientReplicator.RequestData():andThen(function()\n\tprint(\"Initial snapshot applied\")\nend)\n```\n\n:::caution Call `RequestData()` once\nClients must call `ClientReplicator.RequestData()` at least once to receive the \ninitial snapshot. Otherwise replication will never begin. Subsequent calls are safe no-ops.\n:::\n\n---\n### The config table at a glance\n\n| Field | Purpose |\n| --- | --- |\n| `Data` | A raw table (auto-wrapped in a `TableManager`) or an existing `TableManager`. Defaults to `{}`. |\n| `Targets` | Who a **top-level** replicator sends to: a `Player`, a `{ Player }` list, or `\"all\"`. Pass `{}` to start with none. |\n| `Parent` | A parent `ServerReplicator`, for **child** replicators. Mutually exclusive with `Targets`. |\n| `Namespace` | Optional string (or `ReplicationToken`) used for discovery. Omit for anonymous replicators. |\n| `Tags` | Optional `{ [string]: any }` metadata for filtering in `ForEach`/`GetAll`/`GetFirst`. |\n| `Coalesced` | Send only the latest op per key each frame, dropping intermediate writes. |\n| `ImmediateFlush` | Send each op immediately instead of batching per frame. |\n| `Client` | Declares custom remotes (signals/functions). See [TR Custom Remotes](/api/TR%20Custom%20Remotes). |\n\n\n### Things to be aware of\n\n:::caution Exactly one of `Targets` or `Parent`\nA top-level replicator **must** be given `Targets` (pass `{}` for none). A child\nreplicator **must** be given `Parent` and inherits its ancestor's targets. Passing\nboth, or neither, throws.\n:::\n\n- **Always destroy when done.** Call `replicator:Destroy()` when a replicator is no\n  longer needed. It also listens to its manager's `OnDestroy`, so destroying the\n  manager tears the replicator down too.\n- **Client code never creates or destroys replicators.** Lifetime is server-driven;\n  calling `Destroy` on a `ClientReplicator` errors.\n- **Call `RequestData()` once at startup.** Subsequent calls are safe no-ops.\n- **Setting up `OnNew` listeners before `RequestData()`.** Listeners added\n  after it resolves may miss replicators that were in the initial snapshot since.\n  `OnNew` only fires for replicators that arrive after the listener is registered.\n\n---\n### See also\n\n- **[TR Discovery & Targeting](/api/TR%20Discovery%20&%20Targeting)** — finding replicators and controlling who receives them.\n- **[TR Namespaces & Tokens](/api/TR%20Namespaces%20&%20Tokens)** — namespaces and opt-in collision safety.\n- **[TR Parent-Child Guide](/api/TR%20Parent-Child%20Guide)** — nesting replicators into hierarchies.\n- **[TR Custom Remotes](/api/TR%20Custom%20Remotes)** — sending events and functions alongside data.\n- **[TR Performance & Ordering](/api/TR%20Performance%20&%20Ordering)** — batching, coalescing, and ordering guarantees.",
    "source": {
        "line": 117,
        "path": "lib/tablereplicator/src/Docs/TR_Getting_Started.luau"
    }
}