Roact is built on the principle that it’s much easier to build UI declaratively. When something changes, Roact rebuilds the UI virtually and then applies the minimal necessary changes to the actual Roblox Instances. For the vast majority of projects this process is fast enough out of the box — but understanding how it works helps you recognize the rare situations where optimization is worthwhile.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.
How Roact Works
Every time a component renders, Roact produces a virtual tree: a lightweight Lua table description of what the UI should look like. Roact then diffs this new virtual tree against the previous one and calculates the smallest set of changes required. Only those differences are written back to real Roblox Instances. This approach has two important implications:- Rendering is cheap by default. Creating Lua tables and comparing them is far less expensive than creating or modifying Roblox Instances directly.
- Reconciliation has overhead. Every state or prop change triggers a diff pass. On large trees with many components, these passes accumulate — and because Roact runs in Lua, even lightweight operations add up faster than they would in a compiled language.
When Performance Can Become a Concern
For most projects, Roact’s default behavior is more than fast enough. Performance overhead tends to appear only when:- A tree is very large — hundreds of components rendered simultaneously.
- A high-frequency state change (such as a value updating every frame) triggers re-renders across many components that haven’t actually changed.
- Components perform expensive work inside
renderthat runs even when their props or state are identical to the previous values.
The Two Main Optimization Strategies
When optimization is warranted, the techniques available in Roact fall into two categories:Reduce Reconciliation Work
Tell Roact that certain components don’t need to re-render. Use
shouldUpdate or PureComponent to skip diff passes when nothing meaningful has changed.Use Stable Keys
Give child elements stable, unique keys so Roact can match them across renders without having to update every sibling when the list changes order.
Techniques at a Glance
| Technique | What It Does | Best For |
|---|---|---|
shouldUpdate | Custom logic to skip re-renders | Components with complex, known update conditions |
PureComponent | Shallow equality check on props and state | Components used with immutable data (e.g. Rodux) |
| Stable keys | Keeps child Instances matched by identity | Lists where items are added, removed, or reordered |
Optimization Guidelines
Before reaching for any of these tools, consider the following:- Measure first. Roact’s reconciliation is often faster than it appears. Profile your experience before assuming a component is the bottleneck.
- Prefer
PureComponentover manualshouldUpdate. Manual implementations ofshouldUpdateare easy to get wrong and can introduce subtle bugs where components stop updating when they should. - Stable keys are low-risk and high-reward. Switching list keys from numeric indexes to stable IDs rarely introduces bugs and can meaningfully reduce the number of Instance property writes Roblox has to process.
For simple and medium-sized projects, performance is rarely an issue with Roact’s defaults. Premature optimization adds complexity and increases the risk of bugs — particularly with
shouldUpdate, where returning false at the wrong time can cause your UI to silently stop reflecting the current state. Start with clean, readable component code and optimize only when you have a measured reason to do so.