Real-time collaborative editing for TipTap in Svelte with Convex synchronization.
Translated to svelte from React version
Install the package using your preferred package manager:
# npm
npm install cosveti-sync
# yarn
yarn add cosveti-sync
# pnpm
pnpm add cosveti-sync
# bun
bun install cosveti-sync
This package has the following peer dependencies that you'll also need to install:
@tiptap/pm (v3.18.0+)@tiptap/core (v3.18.0+)convex (v1.10.0+)svelte (v5.0.0+)First, set up the backend API in your Convex project. Create a file like convex/convex.config.ts:
import { defineApp } from 'convex/server';
import tiptapSync from 'cosveti-sync/convex.config.js';
const app = defineApp();
app.use(tiptapSync);
export default app;
First, set up the backend API in your Convex project. Create a file like convex/tiptapSync.ts:
import { TiptapSyncClient } from 'cosveti-sync';
import { components } from './_generated/api.js';
const tiptapSync = new TiptapSyncClient(components.tiptapSync);
export const { getSnapshot, submitSnapshot, latestVersion, getSteps, submitSteps } =
tiptapSync.syncApi();
useTiptapSync()The main hook for enabling real-time synchronization.
convex: Convex client instancesyncApi: The sync API object from your Convex backendid: Unique identifier for the documentopts (optional): Options object with the following properties:onSyncError: Callback function for handling synchronization errorssnapshotDebounceMs: Debounce time for snapshot submissions (default: 1000ms)debug: Enable debug logging (default: false)isSyncEnabled: Svelte store for controlling sync enable/disable stateisLoading: Svelte store indicating if the initial content is loadinginitialContent: Svelte store with the initial document contentextension: TipTap extension to add to your editorcreate: Function to create a new document with initial content| Option | Type | Default | Description |
|---|---|---|---|
onSyncError |
(error: Error) => void |
undefined |
Callback for handling synchronization errors |
snapshotDebounceMs |
number |
1000 |
Debounce time in milliseconds for submitting snapshots |
debug |
boolean |
false |
Enable debug logging to console |
When creating the sync API in Convex, you can configure:
checkRead: Function to check read permissionscheckWrite: Function to check write permissionsonSnapshot: Callback when new snapshots are availablepruneSnapshots: Whether to prune old snapshots (default: true)Here's a complete example showing how to integrate the library:
<script lang="ts">
import { useTiptapSync } from 'cosveti-sync/tiptap';
import { api } from '$convex/_generated/api.js';
import type { ConvexClient } from 'convex/browser';
import { useConvexClient } from 'convex-svelte';
import Editor from './Editor.svelte';
// This is if you have used convex-svelte pakage in +layout.svelte to setup the client
const convex: ConvexClient = useConvexClient();
const { isSyncEnabled, extension, isLoading, initialContent, create } = $derived(
useTiptapSync(convex, api.tiptapSyncSvelte, 'test-doc-id')
);
let editor = $state<Editor>();
</script>
{#if $isLoading}
Loading...
{:else if !$initialContent}
<button
onclick={() => {
create({ type: 'doc', content: [] });
}}
>Create doc
</button>
{:else if $extension != undefined}
<div class="py-4 text-center text-xl font-bold">
Shadcn Example
<button
onclick={() => {
isSyncEnabled.update((n) => !n);
console.log($isSyncEnabled);
}}
>
{#if $isSyncEnabled}Pause Sync{:else}Resume Sync{/if}
</button>
<div class="z-50 mt-12 size-full w-screen rounded-md border border-dashed bg-background">
<Editor
class="h-120 max-h-screen overflow-y-scroll pr-2 pl-6"
additionalExtensions={$extension}
content={$initialContent}
bind:editor
/>
</div>
</div>
{/if}
Apache-2.0