SyncedStore Documentation

repository·main·Indexed 23 days ago

https://github.com/yousefed/syncedstore

A CRDT-based library built on top of Yjs for creating collaborative, local-first, and offline-capable applications. It provides an intuitive API for shared data structures and includes bindings for React, Vue, Svelte, and MobX, as well as integration support for rich text editors like TipTap and ProseMirror via XmlFragment.

Tokens
13.3K
Snippets
33
Records
76
Agent score
83%

What's inside SyncedStore

  1. Introduction to SyncedStore

    main

    SyncedStore is a library designed for building distributed, real-time collaborative web applications. It provides an easy-to-use API on top of CRDT (Conflict-free Replicated Data Types) technology, allowing developers to build 'multiplayer' or multi-device experiences with minimal complexity.

    Key benefits include:

    • Collaboration: Automatic synchronization and conflict resolution for multi-user/multi-device experiences.
    • Zero Latency: Operations are handled locally first, with background synchronization.
    • Offline Support: Simplifies building apps that work without a constant internet connection.
    • Decentralization: Facilitates building decentralized applications where users maintain control of their data.

    SyncedStore is built on top of Yjs and Reactive.

  2. Overview of SyncedStore CRDT

    main

    SyncedStore is a library for building collaborative, local-first applications that sync automatically. It is built on top of Yjs, a high-performance CRDT (Conflict-free Replicated Data Type) implementation.

    Key benefits include:

    • Collaboration: Multi-user and multi-device experiences without manual conflict resolution.
    • Performance: Operations are handled locally with zero latency, while synchronization happens in the background.
    • Offline Support: Simplifies building apps that work seamlessly both online and offline.
    • Decentralization: Facilitates building decentralized applications where users maintain control of their data.
  3. What are boxed values and when to use them

    main

    By default, SyncedStore makes every property in an object collaborative and synced independently. This allows multiple users to update different properties of the same object simultaneously.

    However, you should use boxed values if:

    1. Performance is a concern: Every collaborative object incurs bookkeeping overhead. Boxing reduces this by treating the entire object as a single unit.
    2. Data integrity requires atomicity: If your data model requires that properties of an object are updated together rather than independently (e.g., you don't want User A to change a status while User B changes a title at the same time), boxing prevents independent property synchronization.
  4. How SyncedStore abstracts Yjs internals

    main

    SyncedStore provides two primary abstractions to simplify working with Yjs:

    1. Plain JavaScript Data Types: Instead of interacting with Yjs-specific types like Y.Map or Y.Array (e.g., using .set() or .push()), you use standard JavaScript objects and arrays.

      • Example: store.outer.inner.property = value instead of doc.getMap("outer").get("inner").set("property", "value").
    2. Automatic Reactivity: Instead of manually calling .observe() on Yjs types, SyncedStore integrates with Reactive Functional Programming (RFP) libraries. This allows you to automatically observe changes by using autorun or framework-specific hooks/models like useSyncedStore (React), MobX, or Vue's reactive model.

  5. How SyncedStore integrates with React

    main
    In this React example, the collaborative state is managed through a central store definition (typically in a file like src/store.ts). React components consume and react to changes in this shared state using the useSyncedStore hook. This hook ensures that components are automatically re-rendered whenever the underlying CRDT data changes, facilitating real-time synchronization across different clients.
  6. Use `SyncedText` for collaborative text editing

    main

    To enable fine-grained, collaborative editing (where multiple users can edit the same string simultaneously without overwriting each other), use the SyncedText class from @syncedstore/core. SyncedText objects behave similarly to Yjs Y.Text instances and provide methods like insert and delete to manipulate text at specific indices.

    import { SyncedText } from "@syncedstore/core";
    
    // Adding a SyncedText property to an existing object
    export const store = syncedStore({ myObject: {} });
    store.myObject.myText = new SyncedText("hello");
    
    // Collaborative editing via index-based methods
    store.myObject.myText.insert(0, "My name is Bob, ");
  7. Implement collaborative rich text editing

    main

    To create a collaborative, Google Docs-style rich text editing experience with SyncedStore, you should bind the store to an XmlFragment. This allows the editor to synchronize complex document structures rather than just plain strings.

    Most modern rich text editors (like TipTap, ProseMirror, or Quill) use Yjs-compatible bindings. When using SyncedStore, you pass the store.fragment (which is an XmlFragment) to the editor's collaboration extension or plugin.

  8. How mutable state works in SyncedStore

    main

    SyncedStore follows a Functional Reactive Programming model, similar to MobX. Instead of using immutable patterns (like setState or useState with spread operators), you mutate variables on the state object directly.

    Example mutations:

    • Adding to an array: state.todos.push({ ... })
    • Updating a property: todo.completed = !todo.completed

    This approach simplifies state updates in React components by removing the need for complex object spreading.

  9. Enable MobX bindings for SyncedStore

    main

    To integrate SyncedStore with the MobX FRP library, you must call enableMobxBindings once during your application setup (e.g., when initializing your store). This allows you to use MobX features like autorun, reaction, and observer directly with your SyncedStore instance.

    Note: This is intended for developers who prefer MobX over the built-in React, Vue, or Reactive bindings.

    import * as mobx from "mobx";
    import { enableMobxBindings } from "@syncedstore/core";
    
    enableMobxBindings(mobx);
  10. Learn more about the underlying CRDT technology

    main

    SyncedStore is built on top of Yjs, a high-performance CRDT implementation. To understand the fundamental principles of Conflict-free Replicated Data Types (CRDTs) that enable SyncedStore's collaborative capabilities, you can explore the following resources:

    • Yjs: The specific CRDT engine used by SyncedStore (yjs.dev).
    • crdt.tech: A comprehensive resource for learning about CRDTs and finding related research and tools (crdt.tech).
    • Local-first software: An essay by Ink & Switch explaining the architectural benefits of local-first software enabled by CRDTs (inkandswitch.com/local-first/).