Skip to content
GitHub

Yjs

Make the editor collaborative by integrating Yjs with ProseKit.

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import 'prosekit/extensions/yjs/style.css'

import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/react'
import { useMemo } from 'react'
import { WebsocketProvider } from 'y-websocket'
import * as Y from 'yjs'

import { defineExtension } from './extension'
import Toolbar from './toolbar'

export default function EditorComponent() {
  const editor = useMemo(() => {
    const doc = new Y.Doc()
    const provider = new WebsocketProvider(
      // Use the public ws server from yjs
      'wss://demos.yjs.dev/ws',
      'github.com/prosekit',
      doc,
    )

    const extension = defineExtension(doc, provider.awareness)
    return createEditor({ extension })
  }, [])

  return (
    <ProseKit editor={editor}>
      <div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'>
        <Toolbar />
        <div className='relative w-full flex-1 box-border overflow-y-scroll'>
          <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div>
        </div>
      </div>
    </ProseKit>
  )
}

You need to install yjs and y-prosemirror to use this extension.

pnpm add yjs y-prosemirror
import 'prosekit/extensions/yjs/style.css'
import { defineYjsfunction defineYjs(options: YjsOptions): YjsExtension
@public
} from 'prosekit/extensions/yjs'
import { WebsocketProviderclass WebsocketProvider
Websocket Provider for Yjs. Creates a websocket connection to sync the shared document. The document name is attached to the provided url. I.e. the following example creates a websocket connection to http://localhost:1234/my-document-name
@example import * as Y from 'yjs' import { WebsocketProvider } from 'y-websocket' const doc = new Y.Doc() const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)@extendsObservableV2<{ 'connection-close': (event: CloseEvent | null, provider: WebsocketProvider) => any, 'status': (event: { status: 'connected' | 'disconnected' | 'connecting' }) => any, 'connection-error': (event: Event, provider: WebsocketProvider) => any, 'sync': (state: boolean) => any }>
} from 'y-websocket'
import * as Yimport Y from 'yjs' const docconst doc: Y.Doc = new Yimport Y.Doc
new Doc({ guid, collectionid, gc, gcFilter, meta, autoLoad, shouldLoad }?: DocOpts): Y.Doc
export Doc
@paramopts configuration
()
const providerconst provider: WebsocketProvider = new WebsocketProvider
new WebsocketProvider(serverUrl: string, roomname: string, doc: Y.Doc, { connect, awareness, params, protocols, WebSocketPolyfill, resyncInterval, maxBackoffTime, disableBc }?: {
    connect?: boolean | undefined;
    awareness?: Awareness | undefined;
    params?: {
        [x: string]: string;
    } | undefined;
    protocols?: string[] | undefined;
    WebSocketPolyfill?: {
        new (url: string | URL, protocols?: string | string[] | undefined): WebSocket;
        prototype: WebSocket;
        readonly CLOSED: number;
        readonly CLOSING: number;
        readonly CONNECTING: number;
        readonly OPEN: number;
    } | undefined;
    resyncInterval?: number | undefined;
    maxBackoffTime?: number | undefined;
    disableBc?: boolean | undefined;
}): WebsocketProvider
@paramserverUrl@paramroomname@paramdoc@paramopts@paramopts.connect@paramopts.awareness@paramopts.params specify url parameters@paramopts.protocols specify websocket protocols@paramopts.WebSocketPolyfill Optionall provide a WebSocket polyfill@paramopts.resyncInterval Request server state every `resyncInterval` milliseconds@paramopts.maxBackoffTime Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)@paramopts.disableBc Disable cross-tab BroadcastChannel communication
(
'ws://localhost:1234', 'my-roomname', docconst doc: Y.Doc, ) const extensionconst extension: YjsExtension = defineYjsfunction defineYjs(options: YjsOptions): YjsExtension
@public
({ docYjsOptions.doc: Y.Doc
The Yjs instance handles the state of shared data.
, awarenessYjsOptions.awareness: Awareness
The Awareness instance.
: providerconst provider: WebsocketProvider.awarenessWebsocketProvider.awareness: Awareness })