---
name: Collection
last_updated: 2026-11-06T19:09:51Z
type: datatype
summary: "A live, query-based group of instances returned by CollectionService:CreateCollection()."
---

# Collection

A live, query-based group of instances returned by
[CollectionService:CreateCollection()](/docs/reference/engine/classes/CollectionService.md).

**Type:** datatype

## Description

A `Collection` is a group of instances you can attach code to, created with
[CreateCollection(query, root)](/docs/reference/engine/classes/CollectionService.md).
The collection tracks every descendant of `root` (default [Workspace](/docs/reference/engine/classes/Workspace.md))
that matches `query`, adding instances as they start matching and removing
them when they stop.

You attach code by assigning functions as named callbacks. Callbacks exist for
instance events, property and attribute changes, several [RunService](/docs/reference/engine/classes/RunService.md)
events, and two collection-specific callbacks: `OnAdded` and `OnRemoved`. When
an instance joins, each callback is connected to that instance. When it
leaves, those connections are removed. You write the code once, and the
collection handles per-instance setup and teardown. A collection keeps running
after its variable goes out of scope.

```lua
local CollectionService = game:GetService("CollectionService")

-- Matches every Part under Workspace
local collection = CollectionService:CreateCollection("Part")

-- Fires when a part joins the collection
function collection.OnAdded(part)
	print("Part added to the collection", part)
end

-- Fires when a part in the collection is touched
function collection.OnTouched(part, otherPart)
	print(part, "was touched by", otherPart)
end
```

#### Query Syntax

The query is a CSS-like selector, the same syntax used by
[QueryDescendants()](/docs/reference/engine/classes/Instance.md) and [StyleRule](/docs/reference/engine/classes/StyleRule.md).
See [QueryDescendants()](/docs/reference/engine/classes/Instance.md) for the full
grammar. Comma-separated selectors form a union. The rightmost selector in a
chain is the matched instance, so `Folder > Part` matches the parts, not the
folders.

##### Filters

Multiple filters on one selector must all match.

- `ClassName` — Instances of that class, using [IsA()](/docs/reference/engine/classes/Object.md).
- `#Name` — Instances with that [Name](/docs/reference/engine/classes/Instance.md).
- `.Tag` — Instances with that [CollectionService](/docs/reference/engine/classes/CollectionService.md) tag.
- `[Property = value]` — Instances whose property equals the value.
- `[$Attribute]` or `[$Attribute = value]` — Instances that have the
  attribute, optionally with that value.

Numeric properties and attributes also support `>`, `<`, `>=`, and `<=`.

```lua
-- All Parts
local parts = "Part"
-- Instances named Dog with the tag Scruffy
local mutts = "#Dog.Scruffy"
-- BaseParts named DodgeBall with CanCollide true and attribute Thrown true
local dodgeballs = "BasePart#DodgeBall[CanCollide = true][$Thrown = true]"
```

##### Combinators

- `A > B` — `B` is a child of `A`.
- `A >> B` — `B` is a descendant of `A`.

```lua
-- Parts named Brick whose parent is a Model named Wall
local wallBricks = "Model#Wall > Part#Brick"
-- Parts tagged Leaf anywhere inside a Model named Tree
local leaves = "Model#Tree >> Part.Leaf"
```

##### Pseudo-classes

- `:has(selector)` — Instances with a child or descendant matching `selector`.
- `:not(selector)` — Instances that do not match `selector`.
- `:in-radius(studs, selector)` — [BaseParts](/docs/reference/engine/classes/BasePart.md) within `studs` of
  an instance matching `selector`.

```lua
-- Instances without an OnFire attribute
local notBurning = ":not([$OnFire])"
-- MeshParts with a child tagged SwordPart
local sheaths = "MeshPart:has(> .SwordPart)"
-- Parts tagged FireBall within 10 studs of a character
local nearbyFireballs = "BasePart.FireBall:in-radius(10, Model:has(> Part#HumanoidRootPart))"
```

#### Callbacks

Assign a callback with function-statement syntax:

```lua
local CollectionService = game:GetService("CollectionService")
local collection = CollectionService:CreateCollection("Part")

function collection.OnAdded(part)
	-- Fires when a part joins the collection
end
```

Each callback can be assigned **only once**. Reassigning it raises an error,
so a collection's behavior can't be silently replaced elsewhere.

##### OnAdded and OnRemoved

- `OnAdded(instance)` — Fires when an instance starts matching the query. When
  you first assign `OnAdded`, it also fires once for every instance already in
  the collection.
- `OnRemoved(instance)` — Fires when an instance stops matching. For example,
  its tag is removed, it leaves the root, it is destroyed, the collection is
  destroyed, or [Enabled](/docs/reference/engine/datatypes/Collection.md) is set to `false`.

##### Membership Guarantees

Collections guarantee that:

1. An instance is visible as a member only after its `OnAdded` has run.
2. Callbacks fire only on instances that currently match the query.
3. `OnRemoved` always fires when an instance stops matching.

Together, these let `OnAdded` and `OnRemoved` act as a constructor and
destructor. Per-instance data created in `OnAdded` exists for every other
callback, and can always be cleaned up in `OnRemoved`.

Because of guarantee #2, a callback can rely on the query. For example, with
`Part:has(> Part#Handle)`, `instance.Handle` always exists inside a callback.
This also means a pending callback is skipped if its instance stops matching
first. For example, if a property changes and the instance stops matching
before the `OnPropertyChanged` callback runs, that callback is skipped.

Guarantee #3 can be broken only by a crash or abrupt shutdown before
`OnRemoved` runs.

##### RunService Callbacks

These callbacks fire once per step **for each member**. Each receives the
instance and the step's `deltaTime`.

| Collection Callback | Equivalent |
| --- | --- |
| `OnHeartbeat` | [RunService.Heartbeat](/docs/reference/engine/classes/RunService.md) |
| `OnPreRender` | [RunService.PreRender](/docs/reference/engine/classes/RunService.md) |
| `OnPreAnimation` | [RunService.PreAnimation](/docs/reference/engine/classes/RunService.md) |
| `OnPreSimulation` | [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md) |
| `OnPostSimulation` | [RunService.PostSimulation](/docs/reference/engine/classes/RunService.md) |

The collection connects to each [RunService](/docs/reference/engine/classes/RunService.md) event once and calls the
callback for every member, instead of holding one connection per instance.
This runs at about the speed of a loop over a Luau table.

[RunService](/docs/reference/engine/classes/RunService.md) callbacks and [ForEach()](/docs/reference/engine/datatypes/Collection.md)
**cannot yield**. Unlike most non-yielding APIs, they still support debugger
breakpoints.

There are no callbacks for
[BindToSimulation()](/docs/reference/engine/classes/RunService.md) or
[BindToAnimation()](/docs/reference/engine/classes/RunService.md). Instead, call
[ForEach()](/docs/reference/engine/datatypes/Collection.md) inside the binding. This also lets
you set the binding's frequency and priority:

```lua
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")

local collection = CollectionService:CreateCollection("Part")

RunService:BindToSimulation(function(deltaTime)
	collection:ForEach(function(part)
		-- Simulation code
	end)
end, Enum.StepFrequency.Hz60)
```

##### Event, Property, and Attribute Callbacks

A collection can connect a callback to any event, property change, or
attribute change on its members. Here, `X` is the name of the event, property,
or attribute.

- `OnX(instance, ...)` — Connects to event `X` on each member. The callback
  receives the instance, then the event's arguments. For example, `OnTouched`
  connects to [BasePart.Touched](/docs/reference/engine/classes/BasePart.md).
- `OnPropertyChanged.X(instance)` — Fires when property `X` changes, like
  [GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md).
- `OnAttributeChanged.X(instance)` — Fires when attribute `X` changes, like
  [GetAttributeChangedSignal()](/docs/reference/engine/classes/Instance.md).

```lua
local CollectionService = game:GetService("CollectionService")
local collection = CollectionService:CreateCollection("Part.MyTag")

function collection.OnTouched(part, otherPart)
	-- otherPart is the argument from BasePart.Touched
end

function collection.OnPropertyChanged.Color(part)
	-- Fires when the part's Color changes
end

function collection.OnAttributeChanged.MyAttribute(part)
	-- Fires when the part's MyAttribute attribute changes
end
```

A collection can match instances of different classes, so a callback may not
apply to every member:

- Members without the named event or property are skipped. For example, a
  collection of [Parts](/docs/reference/engine/classes/Part.md) and [Folders](/docs/reference/engine/classes/Folder.md) can define
  `OnTouched`, but only the parts respond.
- An event or property name that doesn't exist on any class, such as a
  misspelled `OnTuched`, raises an error when assigned.

#### Lifetime

A collection is not destroyed when its variable goes out of scope. Call
[Destroy()](/docs/reference/engine/datatypes/Collection.md) to stop it. If the collection's root
is destroyed, the collection stops tracking instances and is eventually
garbage collected.

```lua
local CollectionService = game:GetService("CollectionService")
local root = Instance.new("Folder", workspace)

do
	local collection = CollectionService:CreateCollection("Part.MyTag", root)
end
-- The variable is out of scope, but the collection is still running

root:Destroy()
-- The collection stops tracking instances and can be garbage collected
```

#### Typing and Autocomplete

When the query names a class, callbacks receive that type. For example,
`CreateCollection("Part.TagName")` passes a [Part](/docs/reference/engine/classes/Part.md) to its callbacks.

Autocomplete only suggests callbacks that the class supports. `OnTouched` is
suggested for `"Part.TagName"`, but not for a query that can't match a
[BasePart](/docs/reference/engine/classes/BasePart.md). If the query names no class, the first argument is typed as
[Instance](/docs/reference/engine/classes/Instance.md).

## Code Samples

**Collection Lifecycle and Events**

Creates a [Collection](/docs/reference/engine/datatypes/Collection.md) of leaf parts and declares its behavior with
`OnAdded`, `OnRemoved`, [RunService](/docs/reference/engine/classes/RunService.md), event, and property callbacks,
then sets every current leaf green with [Collection:ForEach()](/docs/reference/engine/datatypes/Collection.md).

```lua
local CollectionService = game:GetService("CollectionService")

-- A live collection of parts tagged "MapleLeaf" that are direct children of a model tagged "MapleTree"
local mapleLeaves: Collection = CollectionService:CreateCollection("Model.MapleTree > Part.MapleLeaf")

local function sway(leaf: Part, deltaTime: number)
	leaf.CFrame *= CFrame.fromEulerAnglesXYZ(0, math.rad(20) * deltaTime, 0)
end

local function fall(leaf: Part)
	leaf.Anchored = false
end

-- OnAdded fires once for every leaf already matching, then for each new match
function mapleLeaves.OnAdded(leaf: Part)
	leaf.Material = Enum.Material.Neon
end

-- OnRemoved fires when a leaf stops matching (untagged, reparented out of the tree, or destroyed)
function mapleLeaves.OnRemoved(leaf: Part)
	leaf.Material = Enum.Material.Plastic
end

-- RunService callback: fires once per heartbeat for each leaf in the collection
function mapleLeaves.OnHeartbeat(leaf: Part, deltaTime: number)
	sway(leaf, deltaTime)
end

-- Event callback: connects to BasePart.Touched on each leaf
function mapleLeaves.OnTouched(leaf: Part, other: BasePart)
	fall(leaf)
end

-- Property callback: fires when a leaf's Color changes
function mapleLeaves.OnPropertyChanged.Color(leaf: Part)
	if leaf.Color == Color3.fromRGB(124, 92, 70) then
		fall(leaf)
	end
end

-- Set every leaf currently in the collection green
mapleLeaves:ForEach(function(leaf: Part)
	leaf.Color = Color3.fromRGB(0, 255, 0)
end)
```

## Properties

### Collection.Enabled

**Type:** `bool`

Defaults to `true`. Setting it to `false` fires `OnRemoved` for every
member and stops all callbacks. Setting it back to `true` re-evaluates the
query, reconnects all callbacks, and fires `OnAdded` for every matching
instance. Assigning a non-boolean value raises an error.

### Collection.Query

**Type:** `string`

Read-only. The query passed to
[CreateCollection()](/docs/reference/engine/classes/CollectionService.md).

### Collection.Root

**Type:** `Instance`

Read-only. The root passed to
[CreateCollection()](/docs/reference/engine/classes/CollectionService.md), which
defaults to [Workspace](/docs/reference/engine/classes/Workspace.md). The collection only tracks descendants of
the root.

## Methods

### Collection:Destroy

**Signature:** `Collection:Destroy()`

Fires `OnRemoved` for every member and disconnects all callbacks. After a
collection is destroyed, calling [ForEach()](/docs/reference/engine/datatypes/Collection.md)
raises an error.

### Collection:ForEach

**Signature:** `Collection:ForEach(callback: function)`

Calls `callback` with each member. `ForEach()` iterates a snapshot taken
at call time, so the callback can safely add, remove, or destroy
instances. Instances that start matching during iteration are not visited.
The callback cannot yield. Calling `ForEach()` after the collection is
destroyed raises an error.

**Parameters:**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `callback` | `function` |  | A function called with each instance in the collection. |

### Collection:GetCount

**Signature:** `Collection:GetCount(): number`

Returns the number of instances currently in the collection.

**Returns:** `number` — The number of instances in the collection.