Home › Tips
Sharing State
In Flux, reactivity is independent of the UI tree. A Signal or Store is a standalone value. It doesn't care where it was created or which component reads it.
1. Lifting State Up (Props)
To share state between sibling components, create the Signal in their parent and pass it down as a prop:
local function CounterDisplay(props: { count: Flux.Node<number> })
return Flux.new "TextLabel" {
Size = UDim2.fromOffset(200, 50),
Text = function()
return `Count: {props.count()}`
end,
}
end
local function CounterControls(props: { count: Flux.Node<number> })
return Flux.new "TextButton" {
Size = UDim2.fromOffset(200, 50),
Text = "Increment",
Activated = function()
props.count(props.count() + 1)
end,
}
end
local function App()
local sharedCount = Flux.signal(0)
return Flux.new "Frame" {
Size = UDim2.fromScale(1, 1),
CounterDisplay { count = sharedCount },
CounterControls { count = sharedCount },
}
end2. Ambient Subtree State (Flux.context)
When many components deep in a subtree need the same ambient value (such as a theme, active locale, or modal controller), use Flux.context instead of threading props through every intermediate layer:
local ThemeContext = Flux.context("Dark")
ThemeContext:provide("Light", function()
-- Any descendant created inside this block (including inside Computeds,
-- Effects, and event handlers) reads "Light" from ThemeContext:get()
return MainWindow()
end)3. Global State (ModuleScripts)
Because Roblox caches ModuleScript return values per Luau VM, global client state is simply a ModuleScript that returns a Signal or Store:
State/PlayerSettings.luau
local Flux = require(ReplicatedStorage.Flux)
return Flux.store({
audio = {
masterVolume = 0.5,
musicMuted = false,
},
ui = {
theme = "Dark",
},
})UI/MuteButton.luau
local Flux = require(ReplicatedStorage.Flux)
local settings = require(ReplicatedStorage.State.PlayerSettings)
local function MuteButton()
return Flux.new "TextButton" {
Text = function()
return if settings.audio.musicMuted then "Unmute" else "Mute"
end,
Activated = function()
settings.audio.musicMuted = not settings.audio.musicMuted
end,
}
endTo sync server state into a client Store over a RemoteEvent, pass each incoming table snapshot to Flux.Store.reconcile(store, snapshot) so only changed leaves fire. For reactive signals replicated directly between the server and clients (including per-player state and client-to-server validation), use Flux.Sync.
4. Scoped Feature State (Flux.scope)
When state is shared across a feature (like an inventory window or minigame) that should be completely torn down when closed, create the state and UI inside a Scope:
local function InventorySystem()
local scope, ui = Flux.scope(function()
local selectedItem = Flux.signal(nil :: string?)
return Flux.new "Frame" {
Size = UDim2.fromScale(1, 1),
Sidebar { selected = selectedItem },
ItemGrid { selected = selectedItem },
}
end)
return ui, scope -- call scope:Destroy() when closing the feature
end5. Subscribing Outside Flux (Flux.listen & Flux.on)
To subscribe an imperative callback outside the reactive graph with RBXScriptSignal:Connect semantics (firing on subsequent updates on its own thread via task.spawn), use Flux.listen:
local disconnect = Flux.listen(settings.audio.masterVolume, function(volume)
SoundService.RespectFilteringEnabled = true
masterSoundGroup.Volume = volume
end)
-- Call disconnect() when you want to stop listeningFor synchronous, scope-owned reactions that also run on the initial flush (or receive prevValue), wrap Flux.on in Flux.effect:
local effectNode = Flux.effect(Flux.on(settings.audio.masterVolume, function(volume, prevVolume)
masterSoundGroup.Volume = volume
end))Summary
| Pattern | Best For |
|---|---|
| Props | Passing state from a parent to direct or shallow children |
Flux.context | Ambient values shared across a deep UI subtree (themes, locales) |
ModuleScripts | Client-wide singletons (player settings, session data, round state) |
Flux.Sync | Server-to-client synced state (shared, per-player, or validated writes) |
Flux.scope | Multi-component features with a bounded lifetime (windows, minigames) |
Flux.listen | Bridging reactive updates out to external or yielding systems |