Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Roblox/roact/llms.txt
Use this file to discover all available pages before exploring further.
Roact uses four special constant values as prop marker keys inside the props table of host elements. These markers cannot be expressed as plain string keys because they need to be distinguishable from normal Roblox Instance properties. Each marker is a unique Lua value (a Symbol or a special metatable-driven table) that you index into or use directly as a key inside Roact.createElement. This page documents all four markers, their exact behavior, and the restrictions you should be aware of.
Roact.Event
Roact.Event is a table with a special __index metamethod. Indexing it with any event name returns a unique key object that Roact recognizes as an instruction to connect to that named event on the underlying Roblox Instance.
Roact.Event.EVENT_NAME -- returns a unique key for that event
When Roact mounts or reconciles a host element that has an Event key in its props, it calls Instance:Connect(eventName, callback) under the hood. When the component unmounts, the connection is automatically disconnected — you do not need to manage the connection manually.
Callback signature: The callback receives the Roblox Instance as its first argument, followed by any additional arguments fired by the event signal.
Roact.createElement("ImageButton", {
Size = UDim2.new(0, 100, 0, 100),
[Roact.Event.MouseButton1Click] = function(rbx, x, y)
print(rbx, "was clicked at", x, y)
end,
[Roact.Event.MouseEnter] = function(rbx, x, y)
print("Mouse entered", rbx, "at", x, y)
end,
})
Event callbacks may be triggered during Roact’s reconciliation phase. Calling setState synchronously inside an event handler that fires during reconciliation will cause Roact to attempt a nested re-render, which throws an error. If you need to update state from an event, do so in a way that defers the update (e.g., using a task.defer wrapper), or ensure the event fires outside of reconciliation such as from user input events like MouseButton1Click.
You do not need to disconnect event connections when a component unmounts — Roact handles this automatically for all connections made through Roact.Event.
-- Connecting to multiple events on the same element
local MyButton = Roact.Component:extend("MyButton")
function MyButton:render()
return Roact.createElement("TextButton", {
Text = self.props.label,
Size = UDim2.new(0, 120, 0, 40),
[Roact.Event.Activated] = function(rbx)
-- Activated fires on click or gamepad A — good for cross-platform UIs
if self.props.onActivated then
self.props.onActivated()
end
end,
[Roact.Event.MouseButton2Click] = function(rbx, x, y)
print("Right-clicked at screen position", x, y)
end,
})
end
Roact.Change
Roact.Change works identically to Roact.Event in terms of indexing syntax, but instead of connecting to a named signal it uses Instance:GetPropertyChangedSignal(propertyName) under the hood. This lets you react to changes in a specific Roblox Instance property rather than a general event.
Roact.Change.PROPERTY_NAME -- returns a unique key for that property's change signal
Callback signature: The callback receives the Roblox Instance as its only argument. To read the new property value, access it directly on the Instance.
Roact.createElement("ScrollingFrame", {
Size = UDim2.new(1, 0, 1, 0),
CanvasSize = UDim2.new(0, 0, 2, 0),
[Roact.Change.CanvasPosition] = function(rbx)
print("Scrolled to", rbx.CanvasPosition)
end,
})
Property changed signals fire during Roact’s reconciliation pass, because Roact itself sets Instance properties at that time. Triggering a re-render (via setState) inside a Roact.Change handler while reconciliation is in progress will cause Roact to throw an error about nested renders. Use Roact.Change carefully: avoid patterns where setting a prop causes a property change that immediately schedules another render.
-- Tracking a TextBox's text as the user types
local SearchBar = Roact.Component:extend("SearchBar")
function SearchBar:init()
self:setState({ query = "" })
end
function SearchBar:render()
return Roact.createElement("TextBox", {
PlaceholderText = "Search...",
Size = UDim2.new(1, 0, 0, 36),
Text = self.state.query,
[Roact.Change.Text] = function(rbx)
-- This fires from user input, not from reconciliation, so setState is safe
self:setState({ query = rbx.Text })
end,
})
end
Roact.Ref
Roact.Ref is a Symbol constant used as a key in the props table of a host element to give you direct access to the underlying Roblox Instance. It is not valid on function components or stateful components — only on host (string) components.
There are two supported forms: object refs (recommended) and function refs (legacy).
Object refs (recommended)
Create a ref with Roact.createRef(), store it on the component instance (usually in init), pass it as the [Roact.Ref] prop, and then read the Instance via ref:getValue() in didMount or later lifecycle methods. Refs are a kind of binding: Roact automatically writes the Instance into the ref when it mounts, and clears it when the component unmounts.
local MyComponent = Roact.Component:extend("MyComponent")
function MyComponent:init()
self.frameRef = Roact.createRef()
end
function MyComponent:render()
return Roact.createElement("Frame", {
Size = UDim2.new(0, 200, 0, 200),
[Roact.Ref] = self.frameRef,
})
end
function MyComponent:didMount()
-- getValue() returns the live Roblox Instance
local frame = self.frameRef:getValue()
print("Frame is parented to:", frame.Parent:GetFullName())
end
Ref objects have a deprecated field called current that always returns the same value as ref:getValue(). Assigning to ref.current is not allowed and will throw an error. The current field will be removed in a future release of Roact; prefer ref:getValue() for all new code.
Because refs are a kind of binding, they can also be used anywhere Roact accepts a binding for an Instance-type property — Roact will unwrap them to obtain the Instance value.
Function refs (legacy)
You can assign a plain function to [Roact.Ref]. Roact will call the function with the Roblox Instance when the component mounts, and call it again with nil when the component unmounts.
Roact.createElement("TextLabel", {
Text = "Hello",
[Roact.Ref] = function(rbx)
if rbx then
print("Mounted:", rbx:GetFullName())
else
print("Unmounted")
end
end,
})
Function refs have two important limitations:
-
Ordering is not guaranteed. When
Roact.Ref is given a function, Roact does not guarantee when that function will be called relative to the reconciliation of sibling elements or other props on the same element. Do not read a Roblox property inside a function ref callback that is also being set by a Roact prop — you may see either the old or new value.
-
Called with nil on unmount. Your function ref callback will always be called with
nil when the Instance is destroyed. Make sure your callback handles the nil case.
Use object refs created with Roact.createRef() whenever possible to avoid these pitfalls.
-- Forwarding a ref to an underlying host component using Roact.forwardRef
local FancyFrame = Roact.forwardRef(function(props, ref)
return Roact.createElement("Frame", {
Size = props.size or UDim2.new(1, 0, 1, 0),
BackgroundColor3 = props.color or Color3.fromRGB(255, 255, 255),
[Roact.Ref] = ref, -- forward the ref down to the host element
})
end)
-- Usage:
local MyParent = Roact.Component:extend("MyParent")
function MyParent:init()
self.innerRef = Roact.createRef()
end
function MyParent:render()
return Roact.createElement(FancyFrame, {
size = UDim2.new(0, 100, 0, 100),
[Roact.Ref] = self.innerRef,
})
end
function MyParent:didMount()
print("Inner frame:", self.innerRef:getValue())
end
Roact.Children
Roact.Children is the Symbol key that Roact uses internally to store child elements on an element’s props table. When you pass the children argument to Roact.createElement, Roact places that table under the Roact.Children key in the resulting props. In other words:
-- These two calls are exactly equivalent:
Roact.createElement("Frame", props, children)
Roact.createElement("Frame", assign(props, { [Roact.Children] = children }))
In stateful and function components, access children through self.props[Roact.Children] (or props[Roact.Children] in a function component). This lets your component render its children the same way a host element would.
-- A layout wrapper that renders its children inside a UIListLayout container
local function VerticalList(props)
local children = props[Roact.Children] or {}
-- Inject a UIListLayout alongside the caller's children
local allChildren = {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 8),
}),
}
for name, child in pairs(children) do
allChildren[name] = child
end
return Roact.createElement("Frame", {
Size = props.size or UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, allChildren)
end
-- Usage: children are passed as the third argument and become props[Roact.Children]
local ui = Roact.createElement(VerticalList, { size = UDim2.new(0, 200, 0, 400) }, {
First = Roact.createElement("TextLabel", { Text = "Row 1", LayoutOrder = 1 }),
Second = Roact.createElement("TextLabel", { Text = "Row 2", LayoutOrder = 2 }),
Third = Roact.createElement("TextLabel", { Text = "Row 3", LayoutOrder = 3 }),
})
-- Accessing Roact.Children in a stateful component
local Container = Roact.Component:extend("Container")
function Container:render()
local children = self.props[Roact.Children]
return Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 1, 0),
}, children)
end
Use Roact.oneChild(self.props[Roact.Children]) when your component expects exactly zero or one child. It returns the single child element or nil, and throws a descriptive error if more than one child is passed — a helpful guard for components with strict child requirements.