Usage
Reactive is a reactive programming framework for the browser. It gives you a small, predictable model of state, calculations, reverse calculations, control flow and effects, plus a plain DOM driver to project that model onto the page.
The idea
Most frameworks bundle a reactive core together with a large user-interface library: components, layout, widgets, styling. Reactive keeps only the core. It offers a simplified DOM driver that deals in tags, attributes and text. That makes it small, easy to reason about, and simple to drop into an existing page.
The result is a framework you plug in. Mount a reactive component into any element and leave the rest of the page exactly as it is.
Where it comes from
Reactive is a small, hand-written JavaScript library, built from scratch for the browser. Its model, reactive values with readable and writable halves, forward calculations, reverse calculations, and lock-aware readiness, is drawn from a larger reactive system, then refined and tuned to fit JavaScript. The result is a focused runtime you can read in an afternoon.
The runtime primitives live under one object, Reactive: State, Set,
Mount, If, Reverse and the rest. Library modules are their own top-level
globals: you declare one with Reactive.Module, then call it directly, such as
Content.Title(...) or Maths.Add(...). The surface stays small and it runs straight in
the browser with no build step and no imports.
What makes it different
Four choices set Reactive apart. Each links to its full page.
Asynchronous work, handled for you. Loading, errors and retry ride inside every value. A spinner appears around the region that waits, a failed value shows its message and a retry button, and the controls feeding an in-flight write disable themselves. You write the happy path and the framework supplies the rest.
Reversibility in place of event handlers. Interaction is a write. Setting a value runs a reverse back to state, so two-way binding, clicks, typing and retry collapse into one mechanism. You understand a control by reading the binding on it, right where it sits.
Mode-based programming. One definition runs under different modes. Bind different module implementations and the same component renders to the DOM, a PDF, or a 3-D scene, unchanged.
Drivers at the edge. Every connection to the outside world lives in a driver, so the program body stays pure forward code. The running state is plain data, so you can snapshot a program and resume it on another machine, where it reconnects to that machine's drivers.
Principles
- Small surface. A handful of runtime primitives under
Reactive, plus focused modules as their own globals, run straight in the browser. - Plain DOM. A driver over tags, attributes and text.
- Precise updates. Stable identity for state means only what changed is recomputed.
- Async plumbing handled. A value may be ready or still pending; that state travels through calculations for you, so Reactive spans the synchronous and asynchronous cases and deals with both on your behalf.
- Errors ride along. Failure and retry are carried inside every value, so Reactive can catch, retry and recover across a whole calculation while you leave the wiring to it.
- Local reasoning. You think about one component; the framework does the global reasoning that ties the program together.
- Write once, drive anywhere. Swap the modules under a component and the same definition can target the DOM, a PDF, or a 3-D scene unchanged.
- Composable. Focused modules you add as you need them, snapping together for reuse.
Coding standards
Reactive programs are written in a deliberately small, strict subset of JavaScript. Keeping to it lets the framework read your program, reason about readiness, direction and identity for you, and keep the code tidy as it grows. The rules are few:
- Every value is reactive. Do arithmetic and string work through the provided modules:
Maths,Text, and the rest, which give you a rich library of operations that already understand reactive values. You should almost never need to write a raw calculation yourself. - Name every step; keep calls flat. Pass a name, not a nested function call, as an argument.
Bind each intermediate to a
const, one operation per line, and pass the names along. Naming every value keeps the program legible and analysable. - Reach into structures with a dot. Read or write a field of a reactive object with plain
dotted notation:
user.name,order.total. The same expression is both the value to read and, insideReactive.Set, the exact place to write. The dot itself is the field access. - Interaction writes a binding. A button sets a settable; a field is bound to one. To do several things at once, point the button at a reverse.
- Declare, then lay out. Bring your state and derived values into being at the top of a component, then describe the interface underneath, so a component reads top to bottom.
- Callbacks get a block. Write a closure with a braced body,
() => { … }, one statement per line. Every callback is a small function, and looking like one keeps the program easy to read.
Concretely, the strict shape collapses boilerplate. A two-way binding to a field is a single dotted access rather than a hand-written pair of calculations:
// not this — nested calls, a raw Calculate, a manual field rewrite:
const name = Reactive.Binding(
Reactive.Calculate(() => Reactive.Value(user).name),
Reactive.Reverse((n) => Reactive.Set(user, { ...Reactive.Value(user), name: n })));
// this — dotted access is already a settable field, so the input is just:
Reactive.Input.Text(user.name);
user.name reads the field and, because it carries its own
writable half, an input bound to it writes straight back in one expression.