Skip to content

Conditional ​

Control flow, list mapping, and keyed selection: show, showKeyed, switch and presence mount one branch at a time in its own scope; pick and fallback choose between values without mounting anything; forValue and forIndex map reactive arrays; selector tracks which key matches a source in O(1).

luau
Flux.show(loggedIn, function() return Profile {} end, function() return SignIn {} end)

Types ​

Selector type ​

luau
type Selector<K> = setmetatable<{
	--[[
	Reactively reads whether `key` is currently selected, returning a `boolean`.
	Call inside a [computed](https://flux.kohl.gg/guide/concepts/computeds) or
	[effect](https://flux.kohl.gg/guide/concepts/effects) so it re-runs when this key
	gains or loses selection. The shorthand `selector(key)` is equivalent to
	`selector:get(key)`. Each key tracks its own
	[node](https://flux.kohl.gg/guide/concepts/signals), so only the two keys whose
	selection actually flipped re-run; nodes no reaction observes anymore are swept as new keys are read. The
	answer is current even before the next [flush](https://flux.kohl.gg/guide/concepts/effects).
	[Open Documentation](https://flux.kohl.gg/guide/concepts/selectors#reading-a-selection)
	]]
	get: (self: any, key: K) -> boolean,
	--[[
	Tears down the selector: disposes the internal watcher and every per-key
	[node](https://flux.kohl.gg/guide/concepts/signals), then empties the object.
	Call when the source [node](https://flux.kohl.gg/guide/concepts/signals) outlives
	the selector; a selector created inside a
	[scope](https://flux.kohl.gg/guide/concepts/scopes) is cleaned up automatically.
	[Open Documentation](https://flux.kohl.gg/guide/concepts/selectors#cleaning-up)
	]]
	Destroy: (self: any) -> (),
}, { __call: (self: any, key: K) -> boolean }>

Properties ​

otherwise Read Only ​

luau
Conditional.otherwise: any

The catch-all key of a Flux.switch map: its factory mounts whenever the source matches no other key. Every unmatched value shares it, so moving between two unmatched values keeps the branch mounted.

luau
Flux.switch(state) {
	loading = function() return Spinner {} end,
	[Flux.otherwise] = function(value) return Label { text = value } end,
}

Open Documentation

Functions ​

show ​

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

Mounts component while condition is truthy, swapping to fallback (if given) when it turns falsy. Re-evaluates only when condition's truthiness flips, not on every value change. 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

showKeyed ​

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

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 component (or fallback when falsy) 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

switch ​

luau
Conditional.switch<S>(source: S)

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.otherwise (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

presence ​

luau
Conditional.presence<T>(condition: unknown, component: (exiting: Node<boolean>, done: () -> ()) -> T): Node<T?>

Mounts component while condition is truthy and keeps it mounted through its exit transition. component receives (exiting, done): when condition turns falsy, the branch stays mounted with its boolean exiting node set to true, and unmounts once it calls done() (ignored unless exiting), typically when its exit animation settles. A truthy condition before then reverses the exit (exiting returns to false) instead of remounting. The factory runs once per mount in its own branch scope, like Flux.show, and the returned node works at a numeric index of Flux.new / Flux.edit.

Open Documentation

pick ​

luau
Conditional.pick(condition: unknown, whenTrue: any, whenFalse: any?): Node<any>

Derives a node that is whenTrue while condition is truthy and whenFalse otherwise (nil when omitted): the terse form of Flux.computed(function() return if Flux.read(cond) then Flux.read(a) else Flux.read(b) end). Every argument may be a plain value, a node, or a () -> T getter, and only the branch actually taken is read, so the untaken side never becomes a dependency and never re-runs the derivation.

  • Unlike Flux.show this picks a value, not a subtree: nothing is mounted, no branch scope is created, and swapping sides does not rebuild anything.
  • Omit whenFalse for a value that is present only while condition holds.

Open Documentation

fallback ​

luau
Conditional.fallback<V, D>(value: V, default: D): Node<NonNil<Graph.Unwrap<V>> | Graph.Unwrap<D>>

Derives a node that is value while it is non-nil and default otherwise: the terse form of Flux.computed(function() return Flux.read(value) or default end), and the idiomatic way to give an optional prop a default. Either argument may be a plain value, a node, or a () -> T getter, and default is read only while value is nil, so it never becomes a dependency otherwise.

  • Coalesces on nil alone, not on falsiness: a value of false is kept, where or would have replaced it with default. That is the point of reaching for this over or on a Reactive<boolean>? prop.

Open Documentation

forIndex ​

luau
Conditional.forIndex<L, U>(list: L, mapFn: (index: number, item: Node<Item<L>>) -> U): Node<{ U }>

Maps list (a plain array, node, or getter) by index (unkeyed): mapFn(index, item) runs once per position, reusing the mapped result and passing the item 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

forValue ​

luau
Conditional.forValue<L, U>(list: L, mapFn: (index: Node<number>, value: Item<L>) -> U): Node<{ U }>

Maps list (a plain array, node, or getter) by value (keyed): mapFn(index, value) runs once per unique value, caching each item's mapped result 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

selector ​

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

Creates an O(1) keyed selector that tracks which of many keys matches source (a node, () -> S computed, or plain value) for row, tab, or focus selection. 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. By default keys are compared with ==, flipping at most two per-key nodes per change (O(1)); passing a custom equals(sourceValue, key) comparator re-tests every live key on each change (O(live keys)).

Open Documentation

Selector ​

Constructors: Conditional.selector

get ​

luau
Selector:get(key: any): boolean

Reactively reads whether key is currently selected, returning a boolean. Call inside a computed or effect so it re-runs when this key gains or loses selection. The shorthand selector(key) is equivalent to selector:get(key). Each key tracks its own node, so only the two keys whose selection actually flipped re-run; nodes no reaction observes anymore are swept as new keys are read. The answer is current even before the next flush.

Open Documentation

Destroy ​

luau
Selector:Destroy()

Tears down the selector: disposes the internal watcher and every per-key node, then empties the object. Call when the source node outlives the selector; a selector created inside a scope is cleaned up automatically.

Open Documentation