Skip to content


Added in Roact 1.3.0

Roact supports a feature called context which enables passing values down the tree without having to pass them through props. Roact's Context API is based on React's Context API.

Context is commonly used to implement features like dependency injection, dynamic theming, and scoped state storage.

Basic Usage

local ThemeContext = Roact.createContext(defaultValue)

Context objects contain two components, Consumer and Provider.

The Consumer component accepts a render function as its only prop, which is used to render its children. It's passed one argument, which is the context value from the nearest matching Provider ancestor.

If there is no Provider ancestor, then defaultValue will be passed instead.

local function ThemedButton(props)
    return Roact.createElement(ThemeContext.Consumer, {
        render = function(theme)
            return Roact.createElement("TextButton", {
                Size =, 100, 0, 100),
                Text = "Click Me!",
                TextColor3 = theme.foreground,
                BackgroundColor3 = theme.background,

The Provider component accepts a value prop as well as children. Any of its descendants will have access to the value provided to it by using the Consumer component like above.

Whenever the Provider receives a new value prop in an update, any attached Consumer components will re-render with the new value. This value could be externally controlled, or could be controlled by state in a component wrapping Provider:

local ThemeController = Roact.Component:extend("ThemeController")

function ThemeController:init()
        theme = {
            foreground =, 1, 1),
            background =, 0, 0),

function ThemeController:render()
    return Roact.createElement(ThemeContext.Provider, {
        value = self.state.theme,
    }, self.props[Roact.Children])


Legacy Context is a deprecated feature that will be removed in a future release of Roact.

Roact also has a deprecated version of context that pre-dates the stable context API.

Legacy context values do not update dynamically on their own. It is up to the context user to create their own mechanism for updates, probably using a wrapper component and setState.

To use it, add new entries to self._context in Component:init() to create a provider:

local Provider = Roact.Component:extend("FooProvider")

-- Using a unique non-string key is recommended to avoid collisions.
local FooKey = {}

function Provider:init()
    self._context[FooKey] = {
        value = 5,

...and read from that same value in Component:init() in your consumer component:

local Consumer = Roact.Component:extend("FooConsumer")

function Consumer:init() = self._context[FooKey]

function Consumer:render()
    return Roact.createElement("TextLabel", {
        Text = "Foo: " ..,