Reactive CRDT

Reactive CRDT is an easy-to-use library for building collaborative applications that sync automatically. It's built on top of Yjs, a proven, high performance CRDT implementation.
Example
Have a look at the collaborative Todo list examples (React, Vue) to get up to speed. Or, read along for a quick overview.

- Open live demo: React or Vue (Of course, open multiple times to test multiplayer)
- Edit / view on Codesandbox React / Vue
Source in: examples/todo-react and examples/todo-vue.
Quick overview
Setup:
import { crdt, Y } from "@reactivedata/reactive-crdt";
import { WebrtcProvider } from "y-webrtc";
const doc = new Y.Doc();
const webrtcProvider = new WebrtcProvider("my-document-id", doc);
type Vehicle = { color: string; type: string };
export const store = crdt(doc, { vehicles: [] as Vehicle[] });
From now on, the store
object is synced automatically:
User 1:
store.vehicles.push({ type: "car", color: "red" });
User 2 (on a different device):
console.log(store.vehicles.length);
Reacting to updates
Now that State can be modified by connected peers, you probably want to observe changes and automatically display updates. This is easy to do, because Reactive CRDT works closely with the Reactive library.
Let's look at some examples:
Using React
import { useReactive } from "@reactivedata/react";
import { store } from ".";
export default function App() {
const state = useReactive(store);
return (
<div>
<p>Vehicles:</p>
<ul>
{state.vehicles
.map((v) => {
return <li>{v.type}</li>;
})}
</ul>
<input type="text" onKeyPress=((event) => {
if (event.key === "Enter") {
const target = event.target as HTMLInputElement;
// Add a yellow vehicle using the type added in the textfield
state.vehicles.push({ color: "yellow", type: target.value });
target.value = "";
}
})>
</div>
);
}
View on CodeSandbox (coming soon)
Vue
Reactive CRDT works great with Vues reactive programming model. See the Vue Todo example for an example application. In short, just put an object returned by the crdt
function on a Vue data()
object:
import * as Vue from "vue";
import { crdt, Y, useVueBindings } from "@reactivedata/reactive-crdt";
import { WebrtcProvider } from "y-webrtc";
useVueBindings(Vue);
const doc = new Y.Doc();
new WebrtcProvider("id", doc);
export default Vue.defineComponent({
data() {
return {
sharedData: crdt<{
vehicles: Vehicle[];
}>(doc),
regularLocalString: "",
}
}
);
You can now use sharedData.vehicles
in your Vue app and it will sync automatically.
Without framework
You don't have to use React or Vue, you can also use autorun
from the Reactive library to observe changes:
import { reactive, autorun } from "@reactivedata/reactive";
import { store } from ".";
const reactiveStore = reactive(store);
autorun(() => {
reactiveStore.vehicles.forEach((v) => {
console.log(`A ${v.color} ${v.type}`);
});
});
reactiveStore.vehicles.push({ type: "bike", color: "red" });
reactiveStore.vehicles.push({ type: "bus", color: "green" });
View on CodeSandbox (coming soon)
Motivation
Yjs is a very powerful CRDT, but it's API is mostly targeted to create high-performant data bindings for (rich text) editors.
I wanted to explore whether we can abstract the existing Yjs API away, and make it extremely easy to integrate it as a Collaborative Data Store into existing applications.
There were two major design decisions:
- Instead of data types like Y.Map, and Y.Array, can we just use plain Javascript objects and arrays?
- e.g.:
store.outer.inner.property = value
instead of doc.getMap("inner").getMap("outer").getMap("inner").get("value")
- Instead of having to call
.observe
manually, can we integrate with a Reactive Functional Programming library to do this automatically?
- e.g.: wrap your code in
autorun
or use useReactive
(React), or Vue's reactive model and automatically observe all used values from the store.
Would love to hear your feedback!
Credits ❤️
Reactive CRDT builds directly on Yjs and Reactive. It's also inspired by and builds upon the amazing work by MobX and NX Observe.