---
url: /roblox-fundamentals/datamodel-and-instances.md
description: >-
  Navigate the DataModel tree, use properties and events, and manage object
  lifecycles.
---

# DataModel and Instances

*Written: {{ $frontmatter.date }}*

## Learning objectives

* You can describe the [DataModel](https://create.roblox.com/docs/en-us/projects/data-model) tree.
* You can read [Instance](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance) properties and call its methods.
* You can find children safely with [FindFirstChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#FindFirstChild) and [WaitForChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#WaitForChild).
* You can parent and destroy objects without leaks.
* You can choose [Folders](https://create.roblox.com/docs/en-us/projects/data-model) and Models with clear names.
* You can store and read [attributes](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#SetAttribute) on instances.

## Prerequisites

* [First Script](../getting-started/first-script.md)
* [Tables in Depth](../luau-basics/tables-in-depth.md)
* [Functions Advanced](../luau-basics/functions-advanced.md)

## The DataModel tree

Every place is one tree. The [DataModel](https://create.roblox.com/docs/en-us/projects/data-model) sits at the root. You access it with `game` in your code.

Container services form the top branches. [Workspace](https://create.roblox.com/docs/en-us/projects/data-model) holds everything you see in the 3D world. Other branches hold lighting, replicated objects, server objects, and client objects.

Every object in the tree inherits from [Instance](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance). That base class gives you [Name](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Name), [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent), and children. Each class then adds its own behavior on top.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local target = Workspace:FindFirstChild("Baseplate")
if target ~= nil then
	print(target.Name)
end

local children = Workspace:GetChildren()
for index, child in children do
	print(index, child.Name)
end
```

Get [Workspace](https://create.roblox.com/docs/en-us/projects/data-model) once. Look for one child by name. Guard the result with `if`. List direct children with [GetChildren](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#GetChildren). Expected behavior: the Output window prints `Baseplate` when it exists, then prints each top-level child name with its index.

## Properties, methods, and events

A property stores a value. Read it and assign it. A method does work. Call it with arguments. An event signals a change. Connect a function to it.

[Name](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Name) is a non-unique label. You use it to organize the tree. [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent) sets the position in the tree. A child is any object parented to another object. Descendants include children of children.

Dot access like `Workspace.Baseplate` throws when the child is missing. [FindFirstChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#FindFirstChild) returns `nil` instead, so you can guard it. That guard is the pattern you use everywhere.

## Finding children safely

Replication has no guaranteed order. Streaming can delay children further. The [services](https://create.roblox.com/docs/en-us/scripting/services) guide tells you to treat loading order as unknown.

Use [WaitForChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#WaitForChild) for objects that replicate or arrive late. It yields your thread until the child exists. [FindFirstChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#FindFirstChild) with a nil guard suits objects that may or may not exist.

**LocalScript in StarterPlayerScripts**

```luau
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local shared = ReplicatedStorage:WaitForChild("GunConfig")
print(shared.Name)
```

Create a ModuleScript named `GunConfig` in ReplicatedStorage before you play. Get the replicated container once. Wait for the shared object. Print its name. Expected behavior: the client prints `GunConfig` once replication delivers it.

## Parenting and destroy lifecycle

[Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent) controls existence. An object with no parent stays in memory only while your variable holds it. An object parented under `game` lives in the tree.

Set [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent) last when you build objects in code. Each parented change can replicate separately. Setting it last sends one update. The [Instance](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance) reference recommends exactly this order.

[Destroy](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Destroy) ends an object. It sets [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent) to `nil`, locks [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent), disconnects connections, and destroys children. You cannot reuse a destroyed object. Set your variable to `nil` after you destroy it.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local folder = Instance.new("Folder")
folder.Name = "Coins"

local template = Instance.new("Part")
template.Name = "Coin"
template.Size = Vector3.new(1, 1, 1)
template.Anchored = true
template.Parent = folder

folder.Parent = Workspace
```

Create the folder first and set its name. Create the part, set size and anchored state, and parent it to the folder. Parent the folder to [Workspace](https://create.roblox.com/docs/en-us/projects/data-model) last. Expected behavior: a folder named `Coins` appears in Workspace with one anchored part named `Coin` inside it.

## Folders versus Models and naming

Use a [Folder](https://create.roblox.com/docs/en-us/projects/data-model) for organization. It adds no behavior and groups coins, spawn points, or script holders.

Use a Model for a physical set. It groups parts that belong together, like a chair, a table, and a lamp. You can nest models inside models for larger sets.

Give every object a descriptive name. Names keep the tree searchable. Unique sibling names let dot access find the right child. The [data model](https://create.roblox.com/docs/en-us/projects/data-model) guide asks you to name objects descriptively for this reason.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local rack = Instance.new("Model")
rack.Name = "CoinRack"

for count = 1, 3 do
	local coin = Instance.new("Part")
	coin.Name = "Coin" .. tostring(count)
	coin.Size = Vector3.new(1, 1, 1)
	coin.Anchored = true
	coin.Position = Vector3.new(count * 4, 5, 0)
	coin.Parent = rack
end

rack.Parent = Workspace
```

Build a small system. Create one Model. Loop three times. Each pass creates a part, names it `Coin1` through `Coin3`, sizes it, anchors it, positions it, and parents it to the model. Parent the model last. Expected behavior: a model named `CoinRack` appears in Workspace with three spaced coins inside it.

## Store data on instances with attributes

Properties are fixed by the class. [Attributes](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#SetAttribute) are custom key-value pairs you attach to any instance. You use them for tuning numbers, flags, and labels that travel with the object. They accept strings, booleans, numbers, and common value types like Vector3, UDim2, Color3, and BrickColor. The reference lists the exact supported set.

Attributes replicate from the server to every client, so the client can read them but never treat them as proof. Never store secrets in attributes. Store display and tuning data, and re-validate anything that matters on the server. Setting an attribute to nil removes it.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local gate = Instance.new("Part")
gate.Name = "Gate"
gate.Size = Vector3.new(4, 6, 1)
gate.Anchored = true
gate.Position = Vector3.new(0, 3, 20)
gate:SetAttribute("Cost", 25)
gate:SetAttribute("Open", false)
gate.Parent = Workspace

print(gate:GetAttribute("Cost"))
gate:SetAttribute("Open", true)
print(gate:GetAttribute("Open"))
```

```txt
25
true
```

Attach two attributes before parenting. Read `Cost` back as `25`. Flip `Open` to true and read `true`. Expected behavior: Output prints `25` then `true`, and both values ride along with the part to every client. Prefer attributes over a pile of Value objects when the data belongs to the instance itself.

## Common mistakes

**Mistake 1: dot access on a missing child.**

Wrong example below. It indexes a child that may not exist.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local plate = Workspace.Baseplate
print(plate.Name)
```

It fails because dot access throws when `Baseplate` is missing. Replication and renames both cause this. Fix it with [FindFirstChild](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#FindFirstChild) and a nil guard.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local plate = Workspace:FindFirstChild("Baseplate")
if plate then
	print(plate.Name)
end
```

**Mistake 2: parenting before setting properties.**

Wrong example below. It parents first, then changes properties.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local part = Instance.new("Part")
part.Parent = Workspace
part.Size = Vector3.new(4, 4, 4)
part.Anchored = true
```

It works but it replicates extra updates. Each change after parenting can send separately. Fix it by assigning [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent) last.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local part = Instance.new("Part")
part.Size = Vector3.new(4, 4, 4)
part.Anchored = true
part.Parent = Workspace
```

**Mistake 3: reusing a destroyed object.**

Wrong example below. It destroys and then reparents.

**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local old = Workspace:FindFirstChild("OldCoin")
if old then
	old:Destroy()
	old.Parent = Workspace
end
```

It fails because [Destroy](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Destroy) locks [Parent](https://create.roblox.com/docs/en-us/reference/engine/classes/Instance#Parent). The final assignment throws. Fix it by clearing the variable and creating a fresh object when you need one again.

## Exercises

1. List the direct children of Workspace and print each name.
2. Build a folder named `Gems` with two anchored parts, with Parent assigned last.
3. Build a model named `GemShelf` with three parts at different positions, then destroy one part cleanly.

::: details Solution 1
**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local children = Workspace:GetChildren()
for index, child in children do
	print(index, child.Name)
end
```

Expected behavior: Output lists each Workspace child once.
:::

::: details Solution 2
**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local folder = Instance.new("Folder")
folder.Name = "Gems"

for count = 1, 2 do
	local gem = Instance.new("Part")
	gem.Name = "Gem" .. tostring(count)
	gem.Size = Vector3.new(1, 1, 1)
	gem.Anchored = true
	gem.Position = Vector3.new(count * 3, 5, 0)
	gem.Parent = folder
end

folder.Parent = Workspace
```

Expected behavior: a `Gems` folder appears in Workspace with `Gem1` and `Gem2` inside it.
:::

::: details Solution 3
**Script in ServerScriptService**

```luau
--!strict
local Workspace = game:GetService("Workspace")

local shelf = Instance.new("Model")
shelf.Name = "GemShelf"

for count = 1, 3 do
	local gem = Instance.new("Part")
	gem.Name = "Gem" .. tostring(count)
	gem.Size = Vector3.new(1, 1, 1)
	gem.Anchored = true
	gem.Position = Vector3.new(count * 3, 6, 0)
	gem.Parent = shelf
end

shelf.Parent = Workspace

local extra = shelf:FindFirstChild("Gem3")
if extra then
	extra:Destroy()
	extra = nil
end
```

Expected behavior: `GemShelf` appears with two gems left, because `Gem3` is destroyed and cleared.
:::

## Queries without hard references

[CollectionService](https://create.roblox.com/docs/reference/engine/classes/CollectionService) groups instances by string tags instead of stored paths: `AddTag`/`RemoveTag`, `GetTagged` for the current set, `HasTag`/`GetTags` for checks, and `GetInstanceAddedSignal`/`GetInstanceRemovedSignal` for live tracking. Combine `GetTagged` (present) with the added signal (future) so no instance is missed, and always disconnect per-instance connections on the removed signal to avoid leaks. Tags replicate server-to-client as one set, a server add/remove overwrites client-side tags, and streamed-out instances re-sync tags from the server on return, so never treat client tags as authority.

```luau
--!strict
local CollectionService = game:GetService("CollectionService")

local connections: { [Instance]: RBXScriptConnection } = {}

local function watch(coin: Instance): ()
	if coin:IsA("BasePart") then
		connections[coin] = coin.Touched:Connect(function(_hit: BasePart): ()
			print(coin.Name)
		end)
	end
end

CollectionService:GetInstanceAddedSignal("Coin"):Connect(watch)
CollectionService:GetInstanceRemovedSignal("Coin"):Connect(function(coin: Instance): ()
	local connection = connections[coin]
	if connection then
		connection:Disconnect()
		connections[coin] = nil
	end
end)
for _, coin in CollectionService:GetTagged("Coin") do
	watch(coin)
end
```

## Summary

The tree is the game. Read it with guards. Build it parent-last. Destroy it cleanly. Organize it with folders and models. Query it with CollectionService tags without hard paths. Next, learn where code runs in [Scripts and Execution](./scripts-and-execution.md).
