Skip to main content

Introduction

Use these guides to add Yjs-based CRDT to your project with Velt. Pick the React Hook wrapper for the fastest integration in React apps, or the core library for other frameworks and custom implementations.

Setup

Step 1: Install Dependencies

  • React / Next.js
  • Other Frameworks
npm:
npm install @veltdev/crdt-react @veltdev/crdt @veltdev/react
yarn:
yarn add @veltdev/crdt-react @veltdev/crdt @veltdev/react

Step 2: Initialize Velt in your app

Initialize the Velt client by following the Velt Setup Docs. This is required for the collaboration engine to work.

Step 3: Initialize a CRDT store

  • React / Next.js
  • Other Frameworks
import { useVeltCrdtStore } from '@veltdev/crdt-react';

function Component() {
  const { store } = useVeltCrdtStore<string>({
    id: 'my-collab-note',
    type: 'text',
    initialValue: 'Hello, world!',
  });
  return null;
}

Step 4: Set or update the store value

Set or update local changes to the store.
  • React / Next.js
  • Other Frameworks
import { useVeltCrdtStore } from '@veltdev/crdt-react';

function Component() {
  const { update } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
  const onChange = (e) => update(e.target.value);
  return <input onChange={onChange} />;
}

Step 5: Listen for changes

Listen and subscribe for updates from local and remote peers.
  • React / Next.js
  • Other Frameworks
import { useEffect } from 'react';
import { useVeltCrdtStore } from '@veltdev/crdt-react';

function Component() {
  const { store } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });

  useEffect(() => {
    if (!store) return;
    const unsubscribe = store.subscribe((newValue) => {
      console.log('Updated value:', newValue);
    });
    return unsubscribe;
  }, [store]);

  return null;
}

Step 6: Save and restore versions

Create checkpoints and roll back when needed.
  • React / Next.js
  • Other Frameworks
import { useVeltCrdtStore } from '@veltdev/crdt-react';

function Component() {
  const { saveVersion, getVersions, getVersionById, setStateFromVersion } =
    useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });

  async function onSave() {
    const versionId = await saveVersion('Checkpoint');
    console.log('Saved version:', versionId);
  }

  async function onRestoreLatest() {
    const versions = await getVersions();
    if (versions.length === 0) return;
    const latest = versions[0];
    await setStateFromVersion(latest);
  }

  return (
    <div>
      <button onClick={onSave}>Save Version</button>
      <button onClick={onRestoreLatest}>Restore Latest</button>
    </div>
  );
}

APIs

Custom Encryption

Encrypt CRDT data before it’s stored in Velt by registering a custom encryption provider. For CRDT methods, input data is of type Uint8Array | number[].
  • React / Next.js
  • Other Frameworks
async function encryptData(config: EncryptConfig<number[]>): Promise<string> {
  const encryptedData = await yourEncryptDataMethod(config.data);
  return encryptedData;
}

async function decryptData(config: DecryptConfig<string>): Promise<number[]> {
  const decryptedData = await yourDecryptDataMethod(config.data);
  return decryptedData;
}

const encryptionProvider: VeltEncryptionProvider<number[], string> = {
  encrypt: encryptData,
  decrypt: decryptData,
};

<VeltProvider
  apiKey="YOUR_API_KEY"
  encryptionProvider={encryptionProvider}
/>

See also: setEncryptionProvider() · VeltEncryptionProvider · EncryptConfig · DecryptConfig

Initialization

  • React / Next.js
  • Other Frameworks

useVeltCrdtStore

React hook to create and sync a collaborative CRDT store.
const {
  value,
  versions,
  store,
  update,
  saveVersion,
  getVersions,
  getVersionById,
  restoreVersion,
  setStateFromVersion,
} = useVeltCrdtStore<string>({
  id: 'my-collab-note',
  type: 'text',
  initialValue: 'Hello, world!',
  debounceMs: 100,
});

Common Methods

These methods are available in both React and non-React frameworks.

update

Update the store value and sync to peers.
  • Params: newValue: T
  • Returns: void
update('New value');

saveVersion

Save a snapshot of the current state as a named version.
  • Params: versionName: string
  • Returns: Promise<string>
await saveVersion('Checkpoint');

getVersions

Fetch all saved versions.
  • Returns: Promise<Version[]>
const versions = await getVersions();

getVersionById

Fetch a specific version by ID.
  • Params: versionId: string
  • Returns: Promise<Version | null>
const version = await getVersionById('abc123');

setStateFromVersion

Restore the store state from a specific version object.
  • Params: version: Version
  • Returns: Promise<void>
await setStateFromVersion(version);

React-Specific Methods

value

Current value of the store (React hook only).
  • Type: T | null
console.log(value);

versions

List of all stored versions (React hook only).
  • Type: Version[]
console.log(versions);

store

Underlying Velt Store instance (React hook only).
  • Type: Store<T> | null
console.log(store);

restoreVersion()

Restore the store to a specific version by ID (React hook only).
  • Params: versionId: string
  • Returns: Promise<boolean>
await restoreVersion('abc123');

Non-React Specific Methods

getValue

Get the current store value (non-React frameworks only).
  • Returns: T
const value = store.getValue();

subscribe

Subscribe to changes in the store (non-React frameworks only).
  • Params: (newValue: T) => void
  • Returns: () => void (unsubscribe)
const unsubscribe = store.subscribe(console.log);

destroy

Destroy the store, cleaning up resources and listeners (non-React frameworks only).
  • Returns: void
store.destroy();

Yjs Integration Methods

getDoc

Get the underlying Yjs document.
  • Returns: Y.Doc
const ydoc = store.getDoc();

getProvider

Get the provider instance for the store.
  • Returns: Provider
const provider = store.getProvider();

getText

Get the Y.Text instance if store type is ‘text’.
  • Returns: Y.Text | null
const ytext = store.getText();

getXml

Get the Y.XmlFragment instance if store type is ‘xml’.
  • Returns: Y.XmlFragment | null
const yxml = store.getXml();

Developer Tools

VeltCrdtStoreMap

window.VeltCrdtStoreMap is a global debugging interface that exposes all active CRDT stores in your application. It’s automatically created and maintained by the Velt CRDT library, allowing you to inspect, monitor, and debug CRDT stores from the browser console or developer tools.

Availability

The VeltCrdtStoreMap interface is automatically available on the global window object when you initialize any CRDT store.
  • Automatically available in browser environments
  • Exposed on window.VeltCrdtStoreMap when any CRDT store is created
  • Singleton instance (same object reference across all stores)
  • Available immediately after store initialization

Purpose

Use VeltCrdtStoreMap for:
  • Debugging: Inspect store values and state
  • Monitoring: Subscribe to store changes
  • Development: Test and verify CRDT behavior
  • Troubleshooting: Diagnose synchronization issues

API Reference

Methods
get(id?: string): VeltCrdtStore | undefined Get a store by its ID.
  • Params:
    • id (optional): Store ID. If omitted, returns the first registered store.
  • Returns: VeltCrdtStore | undefined - Store instance or undefined if not found
getAll(): { [id: string]: VeltCrdtStore } Get all registered stores as an object.
  • Returns: Object mapping store IDs to store instances
Properties
stores: { [id: string]: VeltCrdtStore } Direct access to all stores (computed property, same as getAll()).

Store Interface

Each store in the map implements the VeltCrdtStore interface: getValue(): any Get the current value from the store.
  • Returns: Current store value (type depends on store type: string, array, map, etc.)
subscribe(callback: (value: any) => void): () => void Subscribe to changes in the store.
  • Params:
    • callback: Function called whenever the store value changes. Receives the new value as parameter
  • Returns: Unsubscribe function to stop listening to changes

Usage Examples

  • React / Next.js
  • Other Frameworks
Basic Inspection
// Check if VeltCrdtStoreMap is available
if (window.VeltCrdtStoreMap) {
  // Get all store IDs
  const storeIds = Object.keys(window.VeltCrdtStoreMap.stores);
  console.log('Available stores:', storeIds);

  // Get a specific store
  const store = window.VeltCrdtStoreMap.get('my-store-id');
  if (store) {
    console.log('Current value:', store.getValue());
  }
}
Monitor Store Changes
// Monitor all stores
Object.keys(window.VeltCrdtStoreMap.stores).forEach(storeId => {
  const store = window.VeltCrdtStoreMap.get(storeId);

  store.subscribe((value) => {
    console.log(`Store "${storeId}" changed:`, value);
  });
});
Debugging Specific Store
// Get store and inspect
const store = window.VeltCrdtStoreMap.get('editor-123');

if (store) {
  // Get current value
  console.log('Current value:', store.getValue());

  // Subscribe to changes
  store.subscribe((value) => {
    console.log('Value updated:', value);
  });

  // Log value every second
  setInterval(() => {
    console.log('Current value:', store.getValue());
  }, 1000);
}
Finding Stores
// Find all stores
const allStores = window.VeltCrdtStoreMap.getAll();
console.log('Total stores:', Object.keys(allStores).length);

// Find stores by pattern
const editorStores = Object.keys(allStores).filter(id =>
  id.startsWith('editor-')
);
console.log('Editor stores:', editorStores);

Events

The library dispatches custom events when stores are registered or unregistered: veltCrdtStoreRegister Fired when a new store is registered.
  • Event Detail: { id: string } // Store ID
  • React / Next.js
  • Other Frameworks
window.addEventListener('veltCrdtStoreRegister', (event) => {
  console.log('Store registered:', event.detail.id);
  const store = window.VeltCrdtStoreMap.get(event.detail.id);
  // Do something with the new store
});
veltCrdtStoreUnregister Fired when a store is unregistered (destroyed).
  • Event Detail: { id: string } // Store ID
  • React / Next.js
  • Other Frameworks
window.addEventListener('veltCrdtStoreUnregister', (event) => {
  console.log('Store unregistered:', event.detail.id);
});