TypeScript · Yjs · MobX

Local models. Shared when you need them.

Build your model with TypeScript classes, relationships, and MobX reactivity. When it joins a document, the same objects gain shared identity and Yjs synchronization. Keep working with them as usual.

Get started Try a demo

Start with a complete local model #

Create objects, organize their relationships, and observe changes. Collections, parent-child relationships, and MobX reactivity all work locally, without creating a document.

import { Plexus, PlexusModel, syncing } from "@here.build/plexus";

@syncing("Task")
class Task extends PlexusModel<Project> {
  @syncing accessor title = "";
  @syncing accessor done = false;
}

@syncing("Project")
class Project extends PlexusModel {
  @syncing.child.list accessor tasks!: Task[];
}

const project = new Project();
const task = new Task({ title: "Ship the editor" });

project.tasks.push(task); // task.parent === project, already locally
task.done = true;        // MobX observers see the change

Add a shared context #

When collaboration becomes part of the application, put that existing model into a document:

const plexus = Plexus.bootstrap(project);

plexus.root === project;      // The same project instance
project.tasks[0] === task;    // The same task instance
task.parent === project;    // The same relationship
task.uuid;                   // Now identifiable across environments

The document adds replicated state and cross-environment identity. A provider can now exchange that state with other clients. Your existing references and observers keep working, including when changes arrive from a peer.

This is progressive enhancement of the model: local objects are useful on their own, and document membership adds the capabilities that need a shared context. Follow the example from local to shared to watch that transition happen.

Useful locally, ready to share #

Move objects with their identity

Add parent-child relationships where your model needs ownership. Moving a block between groups updates both sides, and references still point to it. This works locally too.

Ownership and references →

React to local and remote edits

Observe your local model with MobX. The same observers keep working when the model joins a document and receives changes from other clients.

Reactivity and transactions →

Gain identity across environments

Locally, an object is its own identity. Joining a document adds a UUID so other environments can refer to that same entity.

Local and shared identity →

Where Plexus fits #

Your UI reads and writes Plexus models, and MobX tracks the fields it reads. When those models join a document, Yjs carries their replicated state. A provider exchanges updates between clients.

Responsibility Handled by
Models, ownership, references, document identity Plexus
Replicated document updates Yjs
Reactive reads and derived values MobX, integrated by Plexus
Network connection and reconnection Your Yjs provider
Persistence, authentication, authorization Your application and server

Use your existing Yjs infrastructure, or explore plexus-do for a Cloudflare Durable Object sync server. Connecting clients explains who creates the document and when a client can join.

Try it on a real application #

Drawing editor

Excalidraw

Frames own shapes, arrows reference their endpoints, and the editor keeps its existing React surface. Edit a scene and watch a second client.

Explore the Excalidraw example →

Graph editor

React Flow

Move nodes between groups while edges keep their endpoints. Inspect how ownership and references map onto a graph.

Explore the React Flow example →

Why Plexus exists #

The usual CRDT tutorial works. A TodoMVC-grade app also works. Then a real application introduces related objects, document boundaries, providers, and persistence. Something does not sync, nothing throws, and days disappear.

A Carnegie Mellon user study examined the difficulties JavaScript developers encounter with collaborative data libraries. The discussion under a widely-read Yjs tutorial points to the same gap between a working demo and an application with a substantial data model.

Plexus lets a model grow from local use into collaboration. Classes, collections, relationships, and reactive reads already work in a local application. Joining a document enriches those same objects with replicated state and shared identity. Operations that violate ownership or document invariants throw explicit errors, keeping the model understandable as its context grows.

I can't believe it's not local!