Learn / Scripting

Understanding Index Notation

Learn how dot (.), colon (:), and square bracket ([]) syntax work to index properties, methods, and variables in Roblox Luau.

Prefer to watch? 8 hand-picked Scripting videos →

Beginner4 min readBy Joseph San Nicolas

Every time you write `game.Workspace`, `part:Destroy()`, or `myTable["key"]`, you're doing the same fundamental thing: looking something up inside a table. Luau gives you three syntaxes for it, and knowing which one to reach for (and why) clears up a whole category of confusing errors.

Everything starts with tables

A table is a collection of key/value pairs. The key can be a string, a number, another table, pretty much anything except `nil`.

```lua local player = { Name = "Builderman", Level = 42, Inventory = {"Sword", "Rope"}, } ```

To get a value back out, you index the table with its key.

Square brackets: the literal version

Brackets are the raw form of indexing. Whatever expression you put inside gets evaluated, and the result is used as the key.

```lua print(player["Name"]) --> Builderman print(player["Level"]) --> 42 ```

Because the contents are an expression, you can compute the key at runtime:

```lua local stat = "Level" print(player[stat]) --> 42

local keys = {"Name", "Level"} for _, key in keys do print(key, player[key]) end ```

This is the part people miss. `player[stat]` and `player["stat"]` are completely different. The first looks up whatever string `stat` currently holds. The second looks for a key literally named `"stat"`, which doesn't exist, so you get `nil`.

Brackets are also the only option when your key isn't a valid identifier:

```lua local config = { ["max health"] = 100, -- has a space ["2x-multiplier"] = 2, -- starts with a digit, has a hyphen [1] = "first", -- a number, not a string }

print(config["max health"]) --> 100 print(config[1]) --> first ```

Arrays work this way too. When you write `{"Sword", "Rope"}`, Luau is quietly assigning keys `1` and `2`, so `player.Inventory[2]` gives you `"Rope"`.

Dot: shorthand for a string key

`a.b` is exactly the same as `a["b"]`. Nothing more. It's syntax sugar that saves you typing quotes, and it only works when the key is a valid identifier (letters, digits, underscores, not starting with a digit).

```lua player.Name -- identical to player["Name"] player.Inventory[1] -- mixing both is fine ```

Since the two forms compile to the same lookup, use dot when you're hardcoding a name you already know, and brackets when the key is stored in a variable or has weird characters.

A common bug:

```lua local function getStat(character, statName) return character.statName -- WRONG: looks for a key called "statName" end

local function getStat(character, statName) return character[statName] -- correct end ```

Roblox instances use the same syntax

Instances aren't plain tables, but they're indexed with the same operators. When you write `part.Anchored`, Roblox checks the property list first, then looks for a child named `Anchored`.

```lua local part = workspace.Baseplate -- a child named "Baseplate" print(part.Size) -- a property print(part.Position.Y) -- chained indexing ```

That "child or property" behavior is why dot indexing on instances is risky. If the child hasn't replicated yet or somebody renamed it in Studio, you get:

``` Baseplate is not a valid member of Workspace "Workspace" ```

which throws an error and stops your script. `FindFirstChild` returns `nil` instead, so you can handle the miss:

```lua local target = workspace:FindFirstChild("Baseplate") if target then target.Anchored = true end ```

Use dot for things you're confident exist (services, properties, objects you just created). Use `FindFirstChild` or `WaitForChild` when the object might not be there yet.

Instances also accept bracket syntax, which matters more often than you'd think because Roblox lets you name objects almost anything:

```lua local gui = playerGui["Main HUD"]["Health Bar"] ```

Colon: the method call

Here's where it stops being pure sugar. A colon call passes the thing on the left as the first argument, automatically.

```lua part:Destroy() -- is the same as part.Destroy(part) ```

That hidden first argument is what `self` refers to inside the function. Without it, the method has no idea which object it's supposed to act on.

This is the source of one of the most common beginner errors in Roblox:

```lua part.Destroy() --> Expected ':' not '.' calling member function Destroy ```

Roblox's own API is nice enough to tell you exactly what went wrong. Your own code won't be, so watch for it.

The rule is simple: if it's a function that operates on the object, call it with a colon. If it's a value, index it with a dot. `part.Size` is data. `part:GetMass()` is behavior.

Writing your own methods

The colon works when defining functions too, and it does the mirror-image thing: it creates a hidden `self` parameter.

```lua local Sword = {} Sword.__index = Sword

function Sword.new(damage) local self = setmetatable({}, Sword)