cosveti-sync Svelte Themes

Cosveti Sync

A convex component for syncing tiptap in a svelte project

cosveti-sync

Real-time collaborative editing for TipTap in Svelte with Convex synchronization.

Translated to svelte from React version

Features

  • Real-time synchronization for TipTap editors across multiple clients
  • Convex-powered backend for reliable data synchronization
  • Optimistic updates for smooth user experience
  • Client-side caching with localStorage fallback
  • Configurable sync options and error handling
  • Easy integration with SvelteKit projects
  • Support for pausing/resuming synchronization
  • Prosemirror collab protocol implementation

Installation

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+)

Basic Setup

1. Convex Config

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;

2. Convex Backend Configuration

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

API Documentation

useTiptapSync()

The main hook for enabling real-time synchronization.

Parameters

  • convex: Convex client instance
  • syncApi: The sync API object from your Convex backend
  • id: Unique identifier for the document
  • opts (optional): Options object with the following properties:
    • onSyncError: Callback function for handling synchronization errors
    • snapshotDebounceMs: Debounce time for snapshot submissions (default: 1000ms)
    • debug: Enable debug logging (default: false)

Returns

  • isSyncEnabled: Svelte store for controlling sync enable/disable state
  • isLoading: Svelte store indicating if the initial content is loading
  • initialContent: Svelte store with the initial document content
  • extension: TipTap extension to add to your editor
  • create: Function to create a new document with initial content

Configuration Options

Sync Options

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

Backend API Options

When creating the sync API in Convex, you can configure:

  • checkRead: Function to check read permissions
  • checkWrite: Function to check write permissions
  • onSnapshot: Callback when new snapshots are available
  • pruneSnapshots: Whether to prune old snapshots (default: true)

Example Usage in Svelte

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}

License

Apache-2.0

Top categories

Loading Svelte Themes