Skip to content

Flux

Types

Node type

luau
type Node<T> = Graph.Node<T>

Reactive type

luau
type Reactive<T> = Graph.Reactive<T>

Properties

Bind Module

luau
Flux.Bind: Bind

Bind: low-level hydration primitives behind Flux.model, the _EVENT directive, and child parenting. Binds a node to or from a Roblox Instance property or attribute.

Open Documentation

Color Module

luau
Flux.Color: Color

Color: a perceptual Color3 toolkit working in gamut-clipped Oklab. Provides lighten/darken, mix, rotateHue, and WCAG contrast/readable.

Open Documentation

Defaults Module

luau
Flux.Defaults: Defaults

Defaults: per-class default properties auto-applied to every Roblox Instance built by Flux.new. Mutable.

Open Documentation

Find Module

luau
Flux.Find: Find

Find: Instance selectors for hydration. Child, Descendant, Ancestor, Query, and class-filtered variants apply props to matched descendants of a Roblox Instance.

Open Documentation

Flags Module

luau
Flux.Flags: Flags

Flags: global toggles. Flags.defaults enables or disables the automatic per-class defaults system applied by Flux.new.

Open Documentation

Graph Module

luau
Flux.Graph: Graph

Graph: the low-level reactive graph. Most members are surfaced flat on Flux (Flux.read, Flux.retrack, Flux.untrack, …); reach into the namespace for Flux.flush.

Open Documentation

Interact Module

luau
Flux.Interact: Interact

Interact: transient interaction state. exclusive groups keep at most one boolean node active per group (one open menu, one hovered control); pointIn and sunk hit-test pointer coordinates against Roblox GuiObject bounds and occlusion.

Open Documentation

Layout Module

luau
Flux.Layout: Layout

Layout: layout and appearance helpers (padding, corner, stroke, list, grid, aspectRatio, flex) that return ready-to-parent Roblox Instances with reactive arguments.

Open Documentation

Motion Module

luau
Flux.Motion: Motion

Motion: animation. spring and tween return animated nodes; it also hosts the Color toolkit and step.

Open Documentation

Responsive Module

luau
Flux.Responsive: Responsive

Responsive: viewport-driven UI. viewport, scale, breakpoint, and safeArea are shared reactive nodes that update as the Roblox Camera viewport changes.

Open Documentation

Store Module

luau
Flux.Store: Store

Store: deep reactive proxies over plain tables. Write a nested field and only the nodes whose leaves changed re-run.

Open Documentation

viewport Read Only from Responsive

luau
Flux.viewport: Graph.Node<Vector2>

The active camera's ViewportSize as a node, live on the client.

Open Documentation

scale Read Only from Responsive

luau
Flux.scale: Graph.Node<number>

UIScale factor derived from viewport; 1.0 at config.reference.

Open Documentation

safeArea Read Only from Responsive

luau
Flux.safeArea: Graph.Node<SafeAreaInsets>

Topbar / Core-UI inset pixels from GuiService:GetGuiInset().

Open Documentation

clean Module

luau
Flux.clean: Clean

default Read Only from Conditional

luau
Flux.default: any

Sentinel key for Flux.switch maps: the factory stored under this key mounts whenever the source value matches no other key, the conditional's catch-all branch. Unmatched values share this stable key, so switching between two different unmatched values does not rebuild the branch.

Open Documentation

Functions

edit

luau
Flux.edit<T>(instance: T & Instance)

Hydrates an existing Roblox Instance, applying reactive properties, event handlers, and directives just like Flux.new, but onto an Instance you already have. Returns a function that takes the property table. Called inside a Flux.scope, the bindings are owned by that scope and disconnected when it is destroyed.

Open Documentation

new

luau
Flux.new<T>(className: T | keyof<Type.Creatable>)

Creates a new Roblox Instance of className with per-class defaults applied, then returns a function that takes a property table of static values, reactive nodes, children, and directives. Created inside a Flux.scope, the instance is owned by that scope and destroyed with it.

Open Documentation

flush from Graph

luau
Flux.flush(index: number?)

Drains the pending-effect queue immediately, instead of waiting for the deferred flush. Mainly useful in tests to make effects fire synchronously after a write.

Open Documentation

strict from Graph

luau
Flux.strict(on: boolean?): boolean

Setter/getter for dev-mode strict checks. Pass true/false to set the flag, call with no argument to read it; defaults on in development, off under production. When on:

  • every reactive scope (computeds and effects alike) runs a full second time, surfacing non-idempotent bodies in dev.
  • reactive cycles (re-entering a running scope) and destroying the running scope error.
  • instance binding writes are checked individually: a failure reports the instance, key, and value, and no longer skips the node's remaining bindings.

Open Documentation

scope from Graph

luau
Flux.scope<T>(fn: (scope: Node<any>) -> T): (Node<any>, T)

Runs fn in a fresh owner scope, returning (scope, result): the scope node and whatever fn returned. Computeds, effects, instances, and cleanup callbacks created inside are owned by that scope and torn down together when you call scope:Destroy(); plain signals are not owned and are simply left for the GC.

Open Documentation

signal from Graph

luau
Flux.signal<T>(initial: T, equals: ((T, T) -> boolean)?): Node<T>

Creates a writable signal holding initial.

  • Reading it inside a reaction subscribes; writing a new value re-runs dependents.
  • An optional equals short-circuits propagation when the new value compares equal to the old.
  • A signal is NOT owned by the enclosing scope (unlike a computed/effect); it is reclaimed by the GC when you drop it, not torn down on scope destroy.

Open Documentation

computed from Graph

luau
Flux.computed<T>(fn: () -> T, equals: ((T, T) -> boolean)?): Node<T>

Creates a lazily-cached derived node from fn.

  • it re-evaluates only when read after a tracked dependency changed
  • an optional equals short-circuits propagation on an equal result

Open Documentation

effect from Graph

luau
Flux.effect<T>(fn: (T) -> ()): Node<T>

Creates an effect node derived from fn.

  • deferred: queues fn to run on the next flush, then re-runs it whenever a tracked dependency changes
  • returns the effect node; call node:Destroy() to stop it

Open Documentation

getOwner from Graph

luau
Flux.getOwner(): node?

Returns the current owner scope (a node), or nil at the root.

  • Capture it to re-establish ownership in deferred or async work via Flux.withOwner.

Open Documentation

withOwner from Graph

luau
Flux.withOwner<T>(node: Node<any>, fn: () -> ...T): T

Runs fn with node installed as the current owner scope, so the computeds, effects, and cleanup callbacks created inside attach to it and tear down on its lifetime; dependency tracking is left untouched (tracking-transparent).

Open Documentation

cleanup from Graph

luau
Flux.cleanup(obj: any)

Registers fn to run right before the enclosing reactive scope re-evaluates (and once more when it is destroyed), letting an effect tear down whatever it set up on the previous run: disconnect events, cancel work, release instances.

  • Must be called from inside a computed or effect body; fn receives the node's prior value.

Open Documentation

isNode from Graph

luau
Flux.isNode(object: unknown): boolean

Returns true when obj is a reactive node. Use it to branch on node-or-value props inside a component before deciding whether to read reactively.

Open Documentation

isReactive from Graph

luau
Flux.isReactive(): boolean

Returns true when called from within a tracking computed or effect body, where reads subscribe the running computation.

raw from Graph

luau
Flux.raw(object: any): any

Reads a node's current value without tracking it; a non-node object passes straight through, so it is safe to call on a prop that may or may not be reactive.

Open Documentation

read from Graph

luau
Flux.read(object: any): any

Reads a node's current value with tracking, just like node:get(); a non-node object passes straight through, making it the idiomatic way to read a prop that may be a plain value or a node.

Open Documentation

untrack from Graph

luau
Flux.untrack<T>(fn: (() -> ...T)?): ...T

Suspends dependency tracking for the duration of fn (reads inside don't subscribe the enclosing computed/effect).

  • Called bare as untrack() it suspends imperatively until a matching track() or the next update().

Open Documentation

retrack from Graph

luau
Flux.retrack()

Re-enables dependency tracking after a bare Flux.untrack() call suspended it.

  • Use the imperative untrack() / track() pair to fence a run of un-tracked reads inside a computed or effect.

Open Documentation

on from Bind

luau
Flux.on<D, R>(deps: any, fn: (input: any, prevInput: any?, prevResult: R?) -> R, defer: boolean?): (prevResult: R?) -> R?

Builds an explicit-dependency reaction: it tracks only deps (a single node/function/value or an array of them) and runs fn untracked whenever one changes, so reads inside fn never become dependencies. Wrap it in a computed or effect; fn receives the current input, the previous input, and its own previous result. Set defer to skip the first run and react only to subsequent changes.

Open Documentation

Parameters

  • deps: The dependency, or array of dependencies, to react to.
  • fn: The reaction, run untracked, receiving (input, prevInput, prevResult).
  • defer: Skip the initial run, firing only on later changes.

listen from Bind

luau
Flux.listen<T>(node: Graph.Node<T>, fn: (value: T) -> ()): () -> ()

Registers fn to run on each update of the node, with RBXScriptSignal:Connect semantics: each invocation runs on its own thread (via task.spawn), so callbacks may yield freely. Returns an unsubscribe. For synchronous reactive observation, use an effect instead.

Open Documentation

model from Bind

luau
Flux.model<T>(node: Graph.Node<T>): T

Wraps a reactive node for two-way binding. Assigning Flux.model(node) to a property (or attribute) drives the Instance from the node AND writes the node back whenever the user changes that property, the terse equivalent of assigning the node and repeating it under _EVENT. Only sound for properties that fire a changed signal (e.g. TextBox.Text).

Open Documentation

attr from Directive

luau
Flux.attr(attribute: { [string]: any } | string, value: any?): any

One-off form of the _ATTR directive: place Flux.attr("Name", value) or Flux.attr { Name = value } anywhere in the array portion of a property table to set or bind Roblox attributes on the Instance. Accepts the same values as _ATTR (static values, reactive nodes, functions, and Flux.model).

Open Documentation

event from Directive

luau
Flux.event(event: { [string]: any } | string, handler: ((...any) -> ()) | Node | nil): any

One-off form of the _EVENT directive: place Flux.event("Name", handler) or Flux.event { Name = handler } anywhere in the array portion of a property table to connect listeners or bind values from the Instance into nodes. Accepts the same values as _EVENT, including a nested _ATTR map for attribute-changed listeners.

Open Documentation

onDestroy from Directive

luau
Flux.onDestroy(...: any): any

One-off form of the _CLEAN directive: place Flux.onDestroy(...) anywhere in the array portion of a property table to tie extra teardown (functions, connections, instances, scopes, or arrays of these) to the Instance's Destroying lifetime. Distinct from Flux.cleanup, which registers a callback on the current reactive owner instead.

Open Documentation

ref from Directive

luau
Flux.ref(ref: ((Instance) -> any) | Node): any

One-off form of the _REF directive: place Flux.ref(nodeOrCallback) anywhere in the array portion of a property table to receive the built Instance. A node is set to the instance; a callback receives it and may return a cleanup value tied to the instance's lifetime.

Open Documentation

tag from Directive

luau
Flux.tag(...: tagValue): any

One-off form of the _TAG directive: place Flux.tag(...) anywhere in the array portion of a property table to manage CollectionService tags on the Instance. Each argument accepts the same values as _TAG: a string, a reactive node or function whose value is a tag or array of tags (diffed on change), or an array of any of these.

Open Documentation

context from Context

luau
Flux.context<T>(default: T): Context<T>

Creates a dynamically-scoped value with the given default, read by any code running under a provide without threading it through arguments. Outside any active provide, reads return default. Pair with components to share theme, services, or the current user down a tree.

Open Documentation

spring from Motion

luau
Flux.spring<T>(target: T, frequency: number?, damping: number?): Graph.Node<T> & Spring<T>

Creates a node that springs toward target, animating any supported value type (numbers, Vector2, UDim2, Color3, …). When target is reactive the spring re-targets on every change, carrying its momentum through. Read the returned node anywhere to subscribe to the live animated value.

Open Documentation

Parameters

  • target: The goal value, static or a reactive node; the spring eases toward it.
  • frequency: Oscillations per second, where higher snaps faster. Defaults to a tuned value; may be reactive.
  • damping: The damping ratio: 1 is critically damped (no overshoot), <1 springs past and settles, >1 is sluggish. May be reactive.

tween from Motion

luau
Flux.tween<T>(target: T, tweenInfo: TweenInfo?): Graph.Node<T>

Creates a node that animates toward target over a fixed duration described by a TweenInfo (easing style, direction, time, repeats). When target or the TweenInfo is reactive the tween restarts toward the new goal. Read the returned node to subscribe to the live animated value. Prefer a Flux.spring for momentum-driven, interruptible motion.

Open Documentation

padding from Layout

luau
Flux.padding(value: Length | PaddingSides | Graph.Node<any> | (() -> Length | Insets)): UIPadding

Creates a UIPadding from a single length (applied to all sides), a per-side { top, bottom, left, right, x, y } table, or a reactive node or function yielding a length or a { top, bottom, left, right } struct such as Flux.safeArea. Plain number values are offset pixels; a UDim is used as-is. Reactive sides bind and update in place; a reactive source may change shape between updates, and nil reads as no padding.

Open Documentation

corner from Layout

luau
Flux.corner(radius: Reactive<Length>?): UICorner

Creates a UICorner rounding the parent's corners. radius is offset pixels or a UDim, optionally reactive; omit it for the engine default (8 px).

Open Documentation

stroke from Layout

luau
Flux.stroke(config: StrokeConfig?): UIStroke

Creates a UIStroke outlining the parent. thickness (px), color, and transparency bind reactively; mode is "contextual" (text outline on text objects, border otherwise) or "border", and joins is "round"/"bevel"/"miter", each also accepting the raw Enum or a reactive source.

Open Documentation

aspectRatio from Layout

luau
Flux.aspectRatio(ratio: Reactive<number>, aspectType: Reactive<"fit" | "scale" | Enum.AspectType>?, dominantAxis: Reactive<"width" | "height" | Enum.DominantAxis>?): UIAspectRatioConstraint

Creates a UIAspectRatioConstraint constraining a frame's width-to-height ratio. Every argument may be reactive.

Open Documentation

Parameters

  • ratio: The aspect ratio; may be a static number or a reactive node.
  • aspectType: "fit" (FitWithinMaxSize) or "scale" (ScaleWithParentSize), or a raw Enum.AspectType.
  • dominantAxis: "width" or "height", or a raw Enum.DominantAxis; an unknown string warns and falls back.

list from Layout

luau
Flux.list(config: ListConfig?): UIListLayout

Creates a UIListLayout that arranges siblings in a line with flexbox semantics. gap is the spacing between items (offset px or a UDim); direction is "x"/"y". align sets the cross-axis and justify the main-axis value: "start"/"center"/"end" align items, "between"/"around"/"evenly" distribute the free space, and "stretch"/"fill" resize items to fill the axis. horizontalAlign/verticalAlign are explicit axis-pinned overrides, wraps flows items onto multiple lines, and lineAlign aligns items within their wrapped line. Every value, including direction, may be reactive; alignments re-route when a reactive direction flips.

Open Documentation

grid from Layout

luau
Flux.grid(config: GridConfig?): UIGridLayout

Creates a UIGridLayout that arranges siblings in a uniform grid. cell is the cell size and gap the spacing, each a Vector2, a number (offset px), or a UDim2. fill is the flow direction "x"/"y"; align/justify map to horizontal/vertical alignment; maxCells caps cells per line. Every value may be reactive.

Open Documentation

flex from Layout

luau
Flux.flex(mode: Reactive<FlexMode | Enum.UIFlexMode>?): UIFlexItem

Creates a UIFlexItem controlling how a child grows or shrinks inside a flex UIListLayout. mode is "fill" (the default when omitted), "grow", "shrink", or "none", or a raw Enum.UIFlexMode, optionally reactive.

Open Documentation

exclusive from Interact

luau
Flux.exclusive(): Group

Creates an exclusive group: at most one boolean node is active per group, so activating a member deactivates the previous one. One group per concern (open menus, hovered controls, modal dialogs) replaces the "close everything else first" bookkeeping those UIs otherwise need.

Open Documentation

pointIn from Interact

luau
Flux.pointIn(x: number, y: number, object: GuiObject): boolean

Whether the point (x, y) lies within object's absolute bounds (inclusive, ignoring rotation). Coordinates are inset-relative, matching InputObject.Position and AbsolutePosition.

Open Documentation

sunk from Interact

luau
Flux.sunk(x: number, y: number, object: GuiObject): boolean

Whether input at (x, y) would be claimed by an Active gui (or GuiButton) rendered above object, e.g. dropping a hover state while a floating menu covers the control. Outside a BasePlayerGui (a plugin dock, a SurfaceGui in workspace) nothing can claim the point, so this yields false.

Open Documentation

async from Async

luau
Flux.async<S, T>(source: any, fetcher: ((S?, T, any) -> T) | T?, initialValue: T?): Async<T>

Creates a non-blocking asynchronous node that runs yielding work off the reactive graph and exposes its progress as four nodes: .data, .error, .loading, and .state. A race guard discards stale results, so only the latest fetch ever writes back.

Open Documentation

Parameters

  • source: A tracked input: a node or a computation whose value is passed to the fetcher; the fetcher re-runs whenever it changes. Omit it (pass the fetcher first) for a one-shot fetch. A nil/false source value gates the fetch off until it becomes truthy.
  • fetcher: The untracked yielding work, called as fetcher(value, previous, refetching) where previous is the previously resolved data and refetching flags a manual refetch. Reactive reads inside it are not tracked as dependencies.
  • initialValue: The starting .data value before the first fetch resolves.

safe from Safe

luau
Flux.safe<T>(tryFn: () -> T, fallback: T | (err: unknown) -> T, equals: ((a: T, b: T) -> boolean)?): Graph.Node<T>

Creates a computed that evaluates tryFn and, if it throws, falls back to fallback, re-running and recovering automatically whenever its tracked dependencies change. fallback may be a static value or a recovery function receiving the caught error; a recovery function runs untracked so it cannot accidentally subscribe to extra dependencies. Pass equals to customize change detection (default ==).

Open Documentation

catch from Safe

luau
Flux.catch<T>(fn: () -> T, handler: (err: unknown) -> T): T

Synchronously runs fn and, if it throws, recovers with handler: a plain try/recover with no reactive node created. Both fn and handler run untracked, so reads inside them don't subscribe the surrounding computed or effect. If handler itself throws, that error propagates.

Open Documentation

forValue from For

luau
Flux.forValue<T, U>(list: Node<{ T }> | { T }, mapFn: (index: Node<number>, value: T) -> U): Node<{ U }>

Maps an array by value (keyed): each item's mapped result is cached against the value itself, so results stay stable across reorders and only the item's reactive index node updates when it moves. Values must be unique: the value is the cache key, so duplicates collapse onto one mapped instance; for primitives, duplicates, or position-based churn use Flux.forIndex instead.

Open Documentation

Parameters

  • list: The reactive array (a node or plain array) to map over.
  • mapFn: Called once per unique value; receives a reactive index node and the value.

forIndex from For

luau
Flux.forIndex<T, U>(list: Node<{ T }> | { T }, mapFn: (index: any, item: Node<T>) -> U): Node<{ U }>

Maps an array by index (unkeyed): the result at each position is built once and reused, with the item passed as a reactive node that updates in place as the value at that index changes. Reach for this over Flux.forValue when the array holds primitives, has duplicates, or churns by position rather than identity.

Open Documentation

Parameters

  • list: The reactive array (a node or plain array) to map over.
  • mapFn: Called once per index; receives the index and a reactive item node.

selector from Selector

luau
Flux.selector<S, K>(source: Graph.Node<S> | (() -> S) | S, equals: ((sourceValue: S, key: K) -> boolean)?): Selector<K>

Creates an O(1) keyed selector for efficiently tracking which of many keys matches a single selected value: selecting a row, a tab, or a focused item. The returned object is callable: selector(key) reactively reads whether key is selected, so flipping the selection re-runs only the two affected keys instead of every observer.

Open Documentation

Parameters

  • source: The tracked selection: a node, a () -> S computed, or a plain value.
  • equals: Tests the source value against a key (default ==). The default path flips at most two per-key nodes per change (O(1)); a custom comparator re-tests every live key per change (O(live keys)).

show from Conditional

luau
Flux.show<T, F>(condition: Node<any> | (() -> any) | any, component: (value: () -> any) -> T, fallback: ((value: () -> any) -> F)?): Node<any>

Mounts component while condition is truthy, swapping to fallback (if given) when it turns falsy. The chosen factory runs once per mount in its own branch scope (the effects and cleanups it registers are torn down automatically on the next swap) and receives a value accessor for the live condition value. Returns a node usable at a numeric index of Flux.new / Flux.edit.

Open Documentation

Parameters

  • condition: The reactive source; re-runs only when its truthiness flips, not on every change.
  • component: Branch factory mounted while condition is truthy; receives (value).
  • fallback: Optional branch factory mounted while condition is falsy; receives (value).

showKeyed from Conditional

luau
Flux.showKeyed<T, F>(condition: Node<any> | (() -> any) | any, component: (value: () -> any) -> T, fallback: ((value: () -> any) -> F)?): Node<any>

Identity-keyed variant of Flux.show: instead of rebuilding only when condition's truthiness flips, it rebuilds whenever the value's identity changes, so swapping one truthy value for another remounts the branch with fresh state. The chosen factory runs in its own branch scope and receives a value accessor; since the value is fixed for the branch's lifetime, reading value() in the factory body yields it directly. Mirrors SolidJS's <Show keyed>.

Open Documentation

Parameters

  • condition: The reactive source; re-runs whenever its value changes identity, not just truthiness.
  • component: Branch factory mounted while condition is truthy; receives (value).
  • fallback: Optional branch factory mounted while condition is falsy; receives (value).

switch from Conditional

luau
Flux.switch<K>(source: Node<K> | (() -> K) | K)

Mounts the branch keyed by source's current value. Curries: pass the reactive source, then a map of value → factory; the matching factory runs once in its own branch scope and receives a value accessor. For a keyed branch value() is the matched key (fixed for the branch's lifetime); under Flux.default (which mounts for any unmatched value), value() tracks the live unmatched value. Returns a node usable at a numeric index of Flux.new / Flux.edit.

Open Documentation

store from Store

luau
Flux.store<T>(initialState: T): T

Wraps a plain table in a deep reactive proxy: reading a field inside a computed or effect subscribes to that field, and assigning a new value re-runs only the dependents of the leaves that changed. Nested plain tables are proxied recursively on access; metatable'd values (Flux nodes, class instances) stay atomic. Available as Flux.store.

Open Documentation

props from Wrap

luau
Flux.props<T, O>(defaults: T, overrides: O?): Wrapped<T>

Builds a component props table from a defaults schema and the caller's overrides, keeping only keys declared in defaults (unknown override keys are left for instance hydration) and passing the merged result through wrap. Neither input is mutated: nested plain tables are deep-copied per call, so instances never share state through the schema or a reused override table.

Open Documentation

wrap from Wrap

luau
Flux.wrap<T>(obj: T): Wrapped<T>

Makes a table reactive in place: every plain leaf becomes a node, recursing into nested plain tables and leaving existing nodes and metatabled tables (a class instance, a proxy) untouched. The table is returned with its type preserved, so wrap({ hp = 100 }).hp() is a number. A non-table value returns a single node holding it.

Open Documentation

Metamethods

__call

luau
Flux.__call<T>(self: any, obj: (() -> T) | T, effectOrProperty: boolean? | string?): Graph.Node<T>

The terse constructor: Flux(value) wraps a static value, Flux(fn) a computed, Flux(fn, true) an effect, and Flux(instance, property) binds a new node from a Roblox Instance property or attribute.

Open Documentation

Parameters

  • obj: A static value, a computation function, or a Roblox Instance to bind from.
  • effectOrProperty: When obj is a function, true marks it an effect; when obj is an Instance, the property or attribute name to bind.