Skip to content

Getting Started ​

Install Flux and build your first reactive UI component in Roblox.

Installation ​

Add Flux to [dependencies] in wally.toml and run wally install:

toml
[dependencies]
Flux = "kohltastrophe/[email protected]"

Flux installs as a shared dependency in Packages, available to both client and server scripts.

GitHub Releases (.rbxm) ​

  1. Download the latest Flux.rbxm from GitHub Releases.
  2. Drag Flux.rbxm into Roblox Studio and place the Flux module inside ReplicatedStorage.

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:

luau
--!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 ​

  1. Flux.signal(0) creates a typed reactive signal (State<number>) holding 0.
  2. count() reads the current value and subscribes the Text binding. Calling count(count() + 1) writes a new value.
  3. Text = function() ... end binds a getter to Text. It sets "Clicks: 0" immediately when created, then batches future updates once per frame (PreRender).
  4. Activated = function() ... end connects the callback to TextButton.Activated.

You can also create untyped nodes with Flux(0) and use arithmetic or string interpolation directly on the node:

luau
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.00.3.0Notes
_ATTR, _TAG, _EVENT, _REF, _CLEAN__ATTR, __TAG, __EVENT, __REF, __CLEANTable 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_NameOne-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.propsFlux.split(props, ...)Split custom component props from pass-through instance props. Nested tables in Flux.new and Flux.edit flatten automatically.
Flux.defaultFlux.otherwiseDefault branch key in Flux.switch.
Flux.safe + Flux.catchFlux.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.safeAreaFlux.Responsive.*Accessed via Flux.Responsive.viewport, scale, breakpoint, displaySize, platform, input, etc.
Flux.pointIn, Flux.sunk, Flux.exclusiveFlux.Interact.*Accessed via Flux.Interact.pointIn, sunk, covered, hover, press, selected, and focused.
Flux.Color.contrastText, spinFlux.Color.readable, rotateHueRenamed 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-way Flux.model bindings, and directives.