Collaboration
Kritzel uses Yjs CRDTs under the hood for real-time collaboration and persistence. All canvas objects are stored in a Yjs document (Y.Doc), enabling conflict-free merging across multiple clients.
Configuring Sync Providers
Pass a syncConfig prop to <kritzel-editor> or <kritzel-engine>:
import { IndexedDBSyncProvider, WebSocketSyncProvider } from 'kritzel-stencil';
const editor = document.querySelector('kritzel-editor');
editor.syncConfig = {
providers: [
IndexedDBSyncProvider,
WebSocketSyncProvider.with({ url: 'wss://your-server.com' }),
],
};
Available Providers
IndexedDBSyncProvider
Persists canvas data locally in IndexedDB. Data survives page reloads and browser restarts.
import { IndexedDBSyncProvider } from 'kritzel-stencil';
editor.syncConfig = {
providers: [IndexedDBSyncProvider],
};
BroadcastSyncProvider
Syncs canvas data across browser tabs using the BroadcastChannel API. Useful for same-browser multi-tab editing.
import { BroadcastSyncProvider } from 'kritzel-stencil';
editor.syncConfig = {
providers: [BroadcastSyncProvider],
};
WebSocketSyncProvider
Real-time sync via a WebSocket server (compatible with y-websocket).
import { WebSocketSyncProvider } from 'kritzel-stencil';
editor.syncConfig = {
providers: [
WebSocketSyncProvider.with({ url: 'wss://your-server.com' }),
],
};
HocuspocusSyncProvider
Real-time sync via a Hocuspocus server. Supports authentication tokens and multiplexed WebSocket connections.
import { HocuspocusSyncProvider } from 'kritzel-stencil';
editor.syncConfig = {
providers: [
HocuspocusSyncProvider.with({
url: 'wss://your-hocuspocus-server.com',
token: 'your-auth-token',
}),
],
};
Combining Providers
Providers can be combined. A common pattern is local persistence + remote sync:
editor.syncConfig = {
providers: [
IndexedDBSyncProvider, // local persistence
BroadcastSyncProvider, // cross-tab sync
WebSocketSyncProvider.with({ url: 'wss://your-server.com' }), // remote sync
],
};
Hocuspocus Multiplexing
When working with multiple documents (e.g. multiple workspaces connecting to different rooms), the Hocuspocus provider supports multiplexing — sharing a single WebSocket connection across all documents.
Setup
import { HocuspocusSyncProvider } from 'kritzel-stencil';
// Step 1: Create a shared WebSocket connection (once)
HocuspocusSyncProvider.createSharedWebSocket({
url: 'ws://localhost:1234',
onConnect: () => console.log('WebSocket connected'),
onDisconnect: () => console.log('WebSocket disconnected'),
});
// Step 2: Create providers that share the connection
const provider1 = new HocuspocusSyncProvider('document-1', doc1, {
token: 'token-for-doc1',
});
const provider2 = new HocuspocusSyncProvider('document-2', doc2, {
token: 'token-for-doc2',
});
await Promise.all([provider1.connect(), provider2.connect()]);
Factory Pattern
const factory = HocuspocusSyncProvider.with({
quiet: false,
onConnect: () => console.log('Connected'),
});
const provider1 = factory.create('document-1', doc1);
const provider2 = factory.create('document-2', doc2);
Cleanup
provider1.destroy();
provider2.destroy();
HocuspocusSyncProvider.destroySharedWebSocket();
Benefits of Multiplexing
- Single WebSocket connection for all documents
- Reduced overhead — no multiple handshakes
- Better resource usage — less memory and network
- Faster document switching — connection already established
- Shared authentication — authenticate once per connection
Hocuspocus Configuration Options
interface HocuspocusOptions {
url?: string; // Server URL (standalone mode only)
name?: string; // Override document name
token?: string | (() => string) | (() => Promise<string>); // Auth token
websocketProvider?: HocuspocusProviderWebsocket; // For multiplexing
quiet?: boolean; // Suppress console logs
forceSyncInterval?: false | number; // Force sync interval (ms)
WebSocketPolyfill?: any; // For Node.js environments
// Callbacks
onConnect?: () => void;
onDisconnect?: () => void;
onSynced?: () => void;
onAuthenticationFailed?: (data: any) => void;
onStatus?: (data: { status: string }) => void;
}