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

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

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.
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.
import { useEffect } from 'react';
import { useVeltCrdtStore } from '@veltdev/crdt-react';

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

  useEffect(() => {
    console.log('Updated value:', value);
  }, [value]);

  return null;
}

Step 6: Save and restore versions

Create checkpoints and roll back when needed.
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[].
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

Core

Initialization

useVeltCrdtStore

React hook to create and sync a collaborative CRDT store.
  • Params: useveltcrdtstore
    • id: Unique identifier for the store.
    • type: Type of Yjs data structure ('text' | 'array' | 'map' | 'xml').
    • initialValue: Optional initial value for the store.
    • debounceMs: Optional debounce time for update propagation (ms).
    • enablePresence: Optional boolean to enable realtime presence tracking (default: true).
  • Returns: Store properties and methods
const {
  value,
  versions,
  store,
  update,
  saveVersion,
  getVersions,
  getVersionById,
  restoreVersion,
  setStateFromVersion,
} = useVeltCrdtStore<string>({
  id: 'my-collab-note',
  type: 'text',
  initialValue: 'Hello, world!',
  debounceMs: 100,
  enablePresence: true,
});

update

Update the store value and sync to peers.
  • Params: newValue: T
  • Returns: void
const { update } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
update('New value');

getValue

Access the current store value.
Get one-time current value
valueReactive property that updates automatically when the store changes.
  • Type: T | null
const { value } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
console.log(value);
Subscribe to value changes
Listen for updates from local and remote peers.
Use useEffect with the reactive value property for automatic updates, or use store.subscribe() for manual subscriptions.
const { store, value } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });

// Option 1: Use reactive value (recommended)
useEffect(() => {
  console.log('Value changed:', value);
}, [value]);

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

store

Access the underlying Velt Store instance for advanced operations.
The underlying store instance returned by the hook.
  • Type: Store<T> | null
const { store } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
console.log(store);

destroy

Clean up resources when done with the store.
Cleanup is handled automatically by the hook when the component unmounts. No manual cleanup is required.
// Cleanup happens automatically when component unmounts
const { store } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });

Versions

saveVersion

Save a snapshot of the current state as a named version.
  • Params: versionName: string
  • Returns: Promise<string>
const { saveVersion } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
await saveVersion('Checkpoint');

getVersions

Fetch all saved versions.
  • Returns: Promise<Version[]>
Reactive property:
const { versions } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
console.log(versions);
Async method:
const { getVersions } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
const versions = await getVersions();

getVersionById

Fetch a specific version by ID.
  • Params: versionId: string
  • Returns: Promise<Version | null>
const { getVersionById } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
const version = await getVersionById('abc123');

setStateFromVersion

Restore the store state from a specific version object.
  • Params: version: Version
  • Returns: Promise<void>
const { setStateFromVersion } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
await setStateFromVersion(version);

restoreVersion

Restore the store to a specific version using its ID.
Convenience method that fetches and restores a version in one call.
  • Params: versionId: string
  • Returns: Promise<boolean>
const { restoreVersion } = useVeltCrdtStore<string>({ id: 'my-collab-note', type: 'text' });
await restoreVersion('abc123');

Yjs

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();

Debugging

Use the Velt Chrome Extension or the window.VeltCrdtStoreMap debugging interface to inspect and monitor your CRDT stores.

window.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.

get()

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
    • Store object methods:
      • getValue(): Get the current value from the store
      • subscribe(callback: (value: any) => void): Subscribe to changes in the store. Returns an unsubscribe function.
// Get a specific store
const store = window.VeltCrdtStoreMap.get('my-store-id');
if (store) {
  console.log('Current value:', store.getValue());
}

// Get the first registered store
const firstStore = window.VeltCrdtStoreMap.get();

getAll()

Get all registered stores as an object.
  • Returns: { [id: string]: VeltCrdtStore } - Object mapping store IDs to store instances
    • Each store object provides:
      • getValue(): Get the current value from the store
      • subscribe(callback: (value: any) => void): Subscribe to changes in the store. Returns an unsubscribe function.
// Get all stores
const allStores = window.VeltCrdtStoreMap.getAll();
console.log('Total stores:', Object.keys(allStores).length);

// Iterate through all stores
Object.entries(allStores).forEach(([id, store]) => {
  console.log(`Store ${id}:`, store.getValue());
});

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
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
window.addEventListener('veltCrdtStoreUnregister', (event) => {
  console.log('Store unregistered:', event.detail.id);
});