Simplest atomic reactive state manager in pure Luau.
quark is a minimal, explicit, and synchronous reactive state library in Luau, focused on clarity and predictability. It provides atomic state objects (atoms) and derived states without any magic scheduling, hidden batching, or lifecycle management.
- Atoms: Basic state holders implemented as simple closures.
- Derived atoms: Read-only states computed from other atoms; automatically update subscribers.
- Push-based reactivity: Subscribers are notified immediately when a state changes.
- Custom guards: Control how state changes are validated and propagated.
- Dynamic dependencies: Derived atoms can change which atoms they depend on during recomputation.
- Synchronous updates: Explicit and predictable—no hidden queues or schedulers.
- No lifecycle or ownership: Atoms are fully unopinionated closures; you control their usage and destruction.
- Lightweight, minimal API:
atom,derived,subscribe.
local quark = require(path_to_quark)
local atom = quark.atom
local derived = quark.derived
local subscribe = quark.subscribe
local applePrice = atom(1)
local money = atom(20)
local applesCanBuy = derived(function(use)
return use(money) / use(applePrice)
end)
subscribe(applesCanBuy, function(amount)
print(`You can buy {amount} apples with {money()} dollars.`)
end, true)
applePrice(2) --> "You can buy 10 apples with 20 dollars."
money(100) --> "You can buy 50 apples with 100 dollars."Creates a reactive state variable.
initial: any non-nil value.custom_guard: optional function(new, old) -> booleanto determine if updates should propagate.- Returns a closure
(value?) -> value.
local counter = atom(0)
counter(1) -- set
print(counter()) -- getCreates a derived reactive state from other atoms.
equation: function (use) -> value. use(atom) reads dependencies.dynamic: optional boolean; allow runtime dependency changes.custom_guard: optional equality guard.
local total = derived(function(use)
return use(price) * use(quantity)
end)Subscribe to state changes.
atom: reactive state or derived atom.callback: function called with new value (value) -> ().eager: optional boolean; if true, immediately calls the callback with current value.
Returns an unsubscriber closure:
local unsubscribe = subscribe(counter, print)
unsubscribe() -- stop listening- Closures are cheap: Every state is a closure; getting and setting is one call.
- Simple equality as default: old ~= new for primitive types; tables are always treated as changed unless a custom guard is provided.
- Derived states ignore sets: Read-only by convention, not enforcement.
- Pure functions expected for derived states: Re-entrancy is guarded but not supported.
- Explicitness over “magic”: No hidden scheduling, batching, or lifecycle management—if your app feels the need for it, it’s a sign of a fundamental design flaw, not a missing feature.
- Dynamic derived states: Dependencies are tracked per recomputation using use, allowing the dependency graph to evolve at runtime.
- Tables are mutable by default: in-place changes require manually calling the atom.
Quark is intentionally minimal and explicit, focusing on clarity over excessive convenience—as expressed in the features and design-philosophy sections.
Use Quark when you want a lightweight, predictable, and fully transparent reactive system. If your application feels the need for automatic batching, hidden schedulers, or complex lifecycle hooks, it likely signals a design problem, not a missing feature.
Install quark by either adding anothersubatomo/quark to your wally.toml file:
[dependencies]
quark = "anothersubatomo/quark@VERSION"Or by copying and pasting the latest version of the source itself, since it's a single file library. 😄