All projects

Cells

10 kB minifiedNo UI framework requiredOpen source

Model async state as a graph.

Cells is the 10 kB TypeScript dataflow runtime behind our products, starting with Chainwall. A Sheet connects writable and computed values, waits for promises, propagates errors, ignores obsolete results, and follows dependencies that change at runtime.

Watch the graph settle when you change an input.

This demo runs Cells with a 620 ms wait after each edit and a 240 ms computation for the sum. Run the race to start a slow result before requesting a newer one.

Readers can use the last stable sum while a new one runs, and subscribers receive the new value after the graph settles.

Graph settled

Change either value.

Cells waits 620 ms after typing stops, then resolves the sum after 240 ms. The last stable result remains readable while the next one runs.

Delayed sum
30

Published after the graph settled

debouncer(620) → delayed(a + b, 240)
Dynamic pointer
10

The downstream cell follows the selected source and keeps its consumer connected when the pointer changes.

Latest result wins

When the slow result arrives, Cells discards it because the request for 9 is newer.

Your state already has dependencies.

Cells records how application values depend on one another and tracks which computations are running or obsolete, so each derived value becomes a node in the graph.

Use promises as values

A source or computation can return a promise, and Cells starts dependent work when the value is ready.

Ignore late results

Cells ranks each computation and discards obsolete results to preserve the value from newer work.

Publish after the graph settles

Cells updates affected nodes in dependency order and notifies subscribers once the update wave has settled.

Follow a changing cell

A computation can return another cell for its consumers to follow as a pointer, supporting selections, routes, caches, and editable records.

Choose how fresh a read must be

Read the stable value now, wait for the current computation, or wait for all work owned by a Sheet or scoped proxy.

Name and remove whole subgraphs

Use a SheetProxy to own and destroy a subgraph as one unit, with cell names and the debugger showing its dependencies during development.

Choose how long a read should wait.

Choose a stable value for a refreshing UI or wait for the current computation in a test or transaction check.

cell.value

Read now

Use the last stable value immediately.

await cell.get()

Wait for first value

Wait only when the cell has not produced a value yet.

await cell.consolidatedValue

Wait for current result

Wait for the computation this cell most recently requested.

await sheet.wait()

Wait for all Sheet work

Wait for every pending computation registered with the Sheet.

Where Cells fits

Use Cells when selections, requests, permissions, and computed views form a changing async pipeline. Dynamic pointers suit routes and editable records. A SheetProxy can remove every cell owned by an unmounted feature.

When another tool fits

Use an event log for replay and audit history, a query cache for standard server data, or a stream library for time-based events. Cells is built for live dependency graphs.

Security labels are still a proposal

We are exploring security labels, value provenance, and protected UI sinks as layers above Cells. Applications must still enforce authorization and validate data at the server or onchain boundary.

Inspect the runtime and its tests.

The repository includes the runtime, package documentation, and tests for async races, pointer changes, and graph propagation.

GitHub