Collaboration
Below is an example of a basic plain text editor using lexical, lexical-vue, and yjs
Tip: you can easily run a local y-websocket server by running:
bash
HOST=localhost PORT=1234 npx y-websocketNote: You shouldn't use LexicalHistoryPlugin with LexicalCollaborationPlugin because LexicalCollaborationPlugin already has history built in.
vue
<script setup lang="ts">
import type { LexicalEditor } from 'lexical'
import { $createParagraphNode, $createTextNode, $getRoot } from 'lexical'
import { LexicalComposer } from 'lexical-vue/LexicalComposer'
import { ContentEditable } from 'lexical-vue/LexicalContentEditable'
import { RichTextPlugin } from 'lexical-vue/LexicalRichTextPlugin'
import { AutoFocusPlugin } from 'lexical-vue/LexicalAutoFocusPlugin'
import { CollaborationPlugin } from 'lexical-vue/LexicalCollaborationPlugin'
import { ref } from 'vue'
import { WebsocketProvider } from 'y-websocket'
import * as Y from 'yjs'
const cursorsContainerRef = ref<HTMLElement | null>(null)
// Optional initial editor state in case collaborative Y.Doc won't
// have any existing data on server. Then it'll user this value to populate editor.
// It accepts same type of values as LexicalComposer editorState
// prop (json string, state object, or a function)
function initialEditorState(editor: LexicalEditor): void {
const root = $getRoot()
const paragraph = $createParagraphNode()
const text = $createTextNode('Welcome to collab!')
paragraph.append(text)
root.append(paragraph)
}
const config = {
// NOTE: This is critical for collaboration plugin to set editor state to null. It
// would indicate that the editor should not try to set any default state
// (not even empty one), and let collaboration plugin do it instead
editorState: null as any,
namespace: 'Demo',
nodes: [],
editable: true,
theme: {},
onError,
}
function providerFactory(id: string, yjsDocMap: Map<string, Y.Doc>) {
const doc = new Y.Doc()
yjsDocMap.set(id, doc)
const provider = new WebsocketProvider('ws://localhost:1234', id, doc)
return provider as any
}
function onError(error: Error) {
throw error
}
</script>
<template>
<LexicalComposer :initial-config="config">
<div ref="cursorsContainerRef" class="editor-container">
<div class="editor-inner">
<RichTextPlugin>
<template #contentEditable>
<ContentEditable class="editor-input" />
</template>
<template #placeholder>
<div class="editor-placeholder">Enter some collaboration text...</div>
</template>
</RichTextPlugin>
<AutoFocusPlugin />
<CollaborationPlugin
id="yjs-plugin"
:provider-factory="providerFactory"
:initial-editor-state="initialEditorState"
:should-bootstrap="true"
:cursors-container-ref="cursorsContainerRef"
/>
</div>
</div>
</LexicalComposer>
</template>