The primary source of performance gains in a Roact application is reducing how much work the reconciler has to do on each update. By default, every state or prop change causes a component to re-render and Roact to diff its output against the previous virtual tree. For components that receive frequent updates — or that sit at the top of a large tree — this can add up quickly. Roact provides three complementary tools for cutting down that work: theDocumentation 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.
shouldUpdate lifecycle method, the PureComponent base class, and stable child keys.
1. The shouldUpdate Lifecycle Method
When a component’s state or props change, Roact calls its shouldUpdate method to decide whether to proceed with a re-render. The default implementation on Roact.Component always returns true:
false in cases where the new props and state don’t require a visual update. When shouldUpdate returns false, Roact skips the render call entirely and leaves the existing virtual tree in place.
shouldUpdate(newProps, newState) receives the incoming props and state as arguments, while self.props and self.state still hold the current values for comparison.
2. PureComponent
Roact.PureComponent is an extension of Roact.Component that ships with a built-in shouldUpdate using shallow equality. Rather than always returning true, it compares each key in the incoming props against the current props (and checks whether the state reference changed) and only returns true when something is actually different.
From the source, the full implementation is:
Roact.PureComponent:extend instead of Roact.Component:extend:
The Inventory Example
Consider the followingInventory and Item components. Inventory holds a list of items in its state and renders one Item element per entry:
- Without PureComponent
- With PureComponent
self.state.items, Inventory re-renders and produces a new element for every Item — including ones whose props haven’t changed at all. With five existing items, adding one triggers six Item renders.3. Stable Keys for Child Elements
The third technique is about how you key the elements in a child list. By default it’s natural to use the loop indexi as the key for each child element:
The Problem with Numeric Index Keys
Suppose your inventory contains two items keyed by their position:Image on the existing ImageLabel at [1] to the potion icon, set Image on [2] to the sword icon, and create a brand new ImageLabel at [3] for the shield. Every existing Instance is touched even though the sword and shield didn’t actually change — they just moved.
Using Stable, Unique Keys
By using the item’s stableid field as the key instead, Roact can track each child across renders by identity:
"sword" and "shield" are the same elements as before — they haven’t been destroyed and recreated, they’ve just moved. The reconciler only needs to update their LayoutOrder properties to reflect their new positions. The Image property on those Instances is not touched at all. Only the new "potion" entry requires a fresh ImageLabel to be created with its Image set.
Here is the full updated Inventory:render using stable keys:
Item components grow more complex. If each Item renders several child elements itself, avoiding a full property-update sweep across all of them on every list change becomes increasingly valuable.
Applying the Techniques Together
The three techniques complement each other and can be layered in the same component tree:Switch Item to PureComponent
Change
Roact.Component:extend to Roact.PureComponent:extend on any component that receives props derived from immutable state. This alone eliminates redundant renders for unchanged items.Key children by stable ID
Replace numeric loop indexes with a stable, unique identifier from your data (such as
item.id). This prevents Roact from re-assigning Roblox Instance properties when list order changes.Add shouldUpdate only when necessary
If a component has update conditions that shallow equality can’t capture — for example, it should skip updates unless a specific combination of props changes — implement a custom
shouldUpdate. Keep the logic simple and test it thoroughly to avoid stale-render bugs.