Asynchronous
Reactive folds asynchronous data into one world. A value is either ready or not, and “not ready yet” travels through your calculations on its own. The same code handles a value that is present and one that is still on its way.
Ready, or still arriving
Every reactive value carries whether it is ready. A value that is still arriving (fetched over the network, read from storage, or otherwise pending) is locked until it lands. Reading a locked value returns at once and reports that it is not ready, rather than blocking, throwing, or handing back a half-formed result.
It propagates for you
Because a calculation knows exactly which inputs it read, a lock on any of them locks the result too. The lock flows forward through everything derived from a pending value, all the way to the DOM, where the driver shows a pending state. When the value arrives, the affected calculations recompute and the locks clear, and the framework tracks it all for you.
// `profile` may still be loading — a locked value until it lands
const name = Reactive.Calculate(() => Reactive.Value(profile).name);
// nothing here asks "has it loaded?" — if profile is not ready,
// name is not ready, and the view shows its pending state on its own
Content.Text(name);
Still arriving
A value is locked while its data is on the way.
Derivations wait
Anything calculated from it is locked in turn, right through to the view.
It lands
The value arrives, the calculations recompute, and the locks clear.
Spinners, errors and retries, for free
The same lock covers failure as well as delay. When something goes wrong (a request errors, a read fails), the value is locked with an error: a message, and usually a retry flag. Like a loading lock, that error propagates forward through every calculation that reads it, all the way to the view.
The driver reads all three states straight off your values and renders them, so the framework supplies the loading and error interface:
A spinner
While anything inside a region is still arriving, the driver lays a loading spinner over it, handling
the isLoading flag and placeholder markup for you.
A pop-up
If a value in that region has failed, the driver surfaces the error and its message in a pop-up, sparing
you try/catch in the view and a toast component to wire up.
A button
When the error carries a retry flag, the pop-up gets a retry button that re-runs what failed, and a combined flag lets one button retry every source that broke at once.
You wrap a region and describe its data; the framework decides, from the values themselves, whether to show a spinner, an error with its message, or a retry, everywhere and consistently. The loading and error interface that normally sprawls across an async app is done for you.
One code path
The result is one version of your code. The values carry readiness and the framework handles it, so you skip
async/await ceremony, isLoading booleans, and separate loading and error
branches scattered through the view. Your logic reads as though every value were already there.
That is what lets you reason about each piece locally, as if it were synchronous, while the whole application stays correct as it grows. Where a value comes from, and whether it has arrived yet, is the framework's concern.