Tutorial · Scripting foundations

How to use attributes in Roblox

Attributes are your own properties on any instance: a Damage number on a sword, a Speed on a platform, an Open true or false on a door, a Level on a player. You can set them in the Properties window without code, read them from any script, and get told when they change. This guide shows how to add them, the four functions you need, what types they hold, how they replicate between the server and players, and when to use them instead of IntValues and StringValues.

Updated 5 November 20265 min readBeginnerLuauWritten by

In short

Select an instance, scroll the Properties window to Attributes, click +, give it a name and a type and set the value. In Luau, instance:GetAttribute("Damage") reads it (nil when it is not set), instance:SetAttribute("Damage", 25) writes it, and instance:GetAttributeChangedSignal("Damage"):Connect(fn) runs fn when it changes. Attributes set on the server replicate to every player; attributes a LocalScript sets stay on that player's device.

  • Attributes hold single values: text, numbers, booleans and Roblox types like Vector3, Color3 and CFrame. Not tables, not instances.
  • Always handle nil from GetAttribute, with a default.
  • Set anything that matters (coins, levels, ownership) on the server only.
On this page
  1. What they are
  2. 1. Read and write
  3. 2. Watch changes
  4. 3. Replication
  5. Attributes vs Values
  6. Common errors
  7. With RoCode
  8. Questions
  9. Sources

What attributes are

Every instance already has built-in properties such as Size and Color. Attributes sit next to them, under their own heading in the Properties window, and are saved with the place. They are the easiest way to give a script a setting without editing its code, and to share a small piece of state between scripts.

Types an attribute can hold
TypeExample valueTypical use
string"RedKey"Names, modes, keys
number25Damage, speed, cooldowns, levels
booleantrueOpen, locked, enabled
Vector3, Vector2, CFrameVector3.new(0, 10, 0)Offsets and positions
Color3, BrickColorColor3.fromRGB(255, 0, 0)Team or rarity colours
UDim, UDim2, NumberRange, NumberSequence, ColorSequence, RectvariesUI and effect settings

Names may use letters and digits, plus periods, hyphens, slashes and underscores, up to 100 characters, and must not start with RBX, which Roblox keeps for itself. Spaces and symbols such as @ or ! are not allowed. Max Speed with a space is not allowed; use MaxSpeed.

Step 1: Read and write attributes

Insert a Part, add a number attribute Damage set to 20 in the Properties window, then put this Script inside it. It reads the setting, uses a default when the attribute is missing or the wrong type, and writes a second attribute from code.

Script in a Part in Workspace named DamagePart
-- Script inside a Part with a number attribute "Damage".
local part = script.Parent :: BasePart

-- A number attribute, with a default when it is missing or not a number
local function numberAttribute(instance: Instance, name: string, default: number): number
	local value = instance:GetAttribute(name)
	return if typeof(value) == "number" then value else default
end

local damage = numberAttribute(part, "Damage", 10)
print("This part deals", damage, "damage")

-- Set an attribute from code (it appears in the Properties window during Play)
part:SetAttribute("TimesTouched", 0)

part.Touched:Connect(function(hit: BasePart)
	local humanoid = hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid")
	if humanoid then
		humanoid:TakeDamage(damage)
		part:SetAttribute("TimesTouched", numberAttribute(part, "TimesTouched", 0) + 1)
	end
end)

-- All attributes at once, as a table
for name, value in part:GetAttributes() do
	print(name, "=", value)
end

Setting an attribute to nil removes it. Reading one that was never set gives nil, which is why the helper checks the type: a typo in the Properties window then falls back to the default instead of breaking the script with attempt to perform arithmetic on nil.

Step 2: React when an attribute changes

GetAttributeChangedSignal(name) fires when that one attribute changes; AttributeChanged fires for any attribute and passes its name. This is how a LocalScript can update a label whenever the server changes a player's level, with no RemoteEvent at all.

Script in ServerScriptService named Levels
-- Script in ServerScriptService named Levels.
-- Keeps a Level attribute on each player; the client shows it.
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player: Player)
	player:SetAttribute("Level", 1)
	-- for this example: level up every 30 seconds
	while player.Parent do
		task.wait(30)
		local level = player:GetAttribute("Level")
		if typeof(level) == "number" then
			player:SetAttribute("Level", level + 1)
		end
	end
end)
LocalScript in StarterPlayerScripts named LevelWatcher
-- LocalScript in StarterPlayerScripts named LevelWatcher.
local Players = game:GetService("Players")

local player = Players.LocalPlayer

local function show()
	print("You are level", player:GetAttribute("Level"))
end

player:GetAttributeChangedSignal("Level"):Connect(show)
show() -- also show the value it already has

Call the function once yourself after connecting, as show() does: the signal only fires on changes, so a value set before your script started would otherwise never be shown.

Step 3: What replicates, and what to trust

Who sees an attribute change
Set byOn an instance thatWho sees the change
A server Scriptreplicates (Workspace, ReplicatedStorage, the Player)The server and every player
A server Scriptis in ServerStorage or ServerScriptServiceThe server only
A LocalScriptanythingOnly that player; the server never sees it

This makes attributes a safe way to show server state on clients, and an unsafe way to receive anything from them. A cheater can set any attribute they like on their own device, but the server keeps its own value, so as long as the server reads its own attributes, a client-side change does nothing. When the client needs to ask for a change, use a RemoteEvent and let the server decide, as the RemoteEvents guide explains.

Attributes or Value objects?

Before attributes existed, the same job was done with child objects such as IntValue, StringValue and BoolValue. Both still work.

When to use which
UseWhen
An attributeSettings and state on an object: damage, speed, open, level. Fewer instances, nothing to WaitForChild.
An IntValue or NumberValueThe leaderboard: Roblox only shows values inside a leaderstats folder, so stats there must be Value objects.
A NumberValueSomething you want to tween smoothly, as the TweenService guide does for models.
An ObjectValuePointing at another instance. Attributes cannot hold instances.
A ModuleScript tableLists and tables of data. Attributes cannot hold tables.

Common errors and fixes

Open the Output window (Window menu, then Output) before you press Play.

GetAttribute returns nil

The name does not match exactly (capitals count), the attribute is on a different instance, or it was set by a LocalScript and you are reading it on the server. Look at the instance's Attributes section during Play, switching between the client and server views.

SetAttribute says the name is invalid

Names may only use letters, digits, periods, hyphens, slashes and underscores, up to 100 characters, and cannot start with RBX. Remove spaces and other symbols.

Attribute is not a supported type

You passed a table or an instance. Store a table in a ModuleScript or as JSON text, and an instance in an ObjectValue.

The client does not see the server's change

The instance is somewhere that does not replicate, such as ServerStorage, or the LocalScript only read the value once. Use GetAttributeChangedSignal.

Build it with RoCode

If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the scripts and instances in your Explorer, so there is no code to paste.

You type

Move the hard-coded damage and cooldown numbers in my sword scripts into Damage and Cooldown attributes on each sword Tool, read on the server with defaults of 20 and 0.5.

RoCode does
  • Can find the numbers you mean with Search Scripts and read the scripts first
  • Can set attributes on your instances and edit the scripts to read them, then compile-check each one with Check Script
  • Sets an undo point before and after each batch of changes, so Ctrl+Z steps back one batch at a time

With plugin 3.9 or newer RoCode can run a short playtest and read the errors, but it checks only what it chose to test, so try the changed objects yourself. How RoCode connects to Studio.

Questions

What are attributes in Roblox?

Custom named values you add to any instance, next to its built-in properties. Scripts read them with GetAttribute, change them with SetAttribute and listen with GetAttributeChangedSignal.

Do attributes replicate to the client?

Yes, when the server sets them on an instance that replicates. Changes a client makes stay on that client.

Can an attribute store a table?

No. Use several attributes, a ModuleScript, or encode the table as text with HttpService:JSONEncode if it really must sit on the instance.

Are attributes faster than IntValues?

They avoid creating extra instances and waiting for children, which helps when you have thousands of objects. For a handful the difference is small; choose by what reads more clearly.

Are attributes saved between game sessions?

Attributes you set in Studio are saved with the place. Values set while the game runs are lost when the server closes; save player data with a data store.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.