Getting Started
Install Flux and build your first reactive UI component in Roblox.
Installation
Wally (Recommended)
Add Flux to [dependencies] in wally.toml and run wally install:
[dependencies]
Flux = "kohltastrophe/flux@0.4.0"Flux installs as a shared dependency in Packages, available to both client and server scripts.
GitHub Releases (.rbxm)
- Download the latest
Flux.rbxmfrom GitHub Releases. - Drag
Flux.rbxminto Roblox Studio and place theFluxmodule insideReplicatedStorage.
File Sync (Rojo)
Copy or submodule the repository's src/ directory into your Rojo tree and require it directly.
Your First App
Create a LocalScript in StarterPlayerScripts:
--!strict
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Flux = require(ReplicatedStorage.Flux)
local new = Flux.new
local function CounterApp()
local count = Flux.signal(0)
return new "ScreenGui" {
Name = "FluxCounterApp",
ResetOnSpawn = false,
Parent = Players.LocalPlayer:WaitForChild("PlayerGui"),
new "TextButton" {
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(250, 80),
BackgroundColor3 = Color3.fromRGB(30, 30, 35),
TextColor3 = Color3.fromRGB(255, 255, 255),
TextSize = 24,
Text = function()
return `Clicks: {count()}`
end,
Activated = function()
count(count() + 1)
end,
},
}
end
CounterApp()Press Play (F5). Clicking the button increments the counter, updating only the button's Text property on the next frame.
How It Works
Flux.signal(0)creates a typed reactive signal (State<number>) holding0.count()reads the current value and subscribes theTextbinding. Callingcount(count() + 1)writes a new value.Text = function() ... endbinds a getter toText. It sets"Clicks: 0"immediately when created, then batches future updates once per frame (PreRender).Activated = function() ... endconnects the callback toTextButton.Activated.
You can also create untyped nodes with Flux(0) and use arithmetic or string interpolation directly on the node:
local count = Flux(0)
-- Inside TextButton:
Text = function() return `Clicks: {count}` end,
Activated = function() count(count + 1) end,See Signals for when to use typed Flux.signal versus terse Flux(0).
Coming from 0.2.0
If you are upgrading an existing project from 0.2.0 to 0.3.0, update these renamed or consolidated APIs:
0.2.0 | 0.3.0 | Notes |
|---|---|---|
_ATTR, _TAG, _EVENT, _REF, _CLEAN | __ATTR, __TAG, __EVENT, __REF, __CLEAN | Table directives now use a double underscore (__EVENT.__ATTR, __EVENT.__TAG). |
Flux.attr, Flux.tag, Flux.event, Flux.ref, Flux.onDestroy | _attr_Name, _tag_Name, _Prop, __attr_Name, __tag_Name | One-off directive helpers are replaced by inline prefix keys and __* directive tables. |
Flux.listen(inst, prop) | Flux.property(inst, key) | Also supports "$Attr" / "_attr_Attr" and "#Tag" / "_tag_Tag" (or Flux(inst, key)). |
Flux(fn, true) | Flux.effect(fn) | Flux() only constructs signals (Flux(v)), computeds (Flux(fn)), and property nodes (Flux(inst, key)). |
Flux.on(deps, fn) | Flux.effect(Flux.on(deps, fn)) | Flux.on now returns a tracked effect callback rather than a standalone listener. |
Flux.wrap / Flux.props | Flux.split(props, ...) | Split custom component props from pass-through instance props. Nested tables in Flux.new and Flux.edit flatten automatically. |
Flux.default | Flux.otherwise | Default branch key in Flux.switch. |
Flux.safe + Flux.catch | Flux.safe(tryFn, catchFn) | Combined into a single error-boundary helper. |
Flux.clean(...) | Flux.dispose(...) | Immediately tears down any Cleanable (use Flux.cleanup(fn) to register on the active owner). |
Flux.viewport, Flux.scale, Flux.safeArea | Flux.Responsive.* | Accessed via Flux.Responsive.viewport, scale, breakpoint, displaySize, platform, input, etc. |
Flux.pointIn, Flux.sunk, Flux.exclusive | Flux.Interact.* | Accessed via Flux.Interact.pointIn, sunk, covered, hover, press, selected, and focused. |
Flux.Color.contrastText, spin | Flux.Color.readable, rotateHue | Renamed on the Oklab color utility namespace. |
Next Steps
- Signals: Hold and update reactive state.
- Computeds: Derive and cache values from other state.
- Effects: Run side effects on frame flushes.
- Scopes: Group instances and reactions under a single
:Destroy(). - Components: Build reusable UI functions and split props with
Flux.split. - Creation: Explore
Flux.new, two-wayFlux.modelbindings, and directives.