🔧 ProgrammierungGitHub Release: rust-lang/rust v1.98.1 (03.09.2026)(03.09.2026 um 15:14 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.5 (11.09.2026)(11.09.2026 um 07:07 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.6 (11.09.2026)(11.09.2026 um 08:08 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.7 (11.09.2026)(11.09.2026 um 10:17 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.8 (11.09.2026)(11.09.2026 um 12:30 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.9 (11.09.2026)(11.09.2026 um 14:01 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.10 (11.09.2026)(11.09.2026 um 17:56 Uhr)
🔧 ProgrammierungGitHub Release: rust-lang/rust v1.98.1 (03.09.2026)(03.09.2026 um 15:14 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.5 (11.09.2026)(11.09.2026 um 07:07 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.6 (11.09.2026)(11.09.2026 um 08:08 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.7 (11.09.2026)(11.09.2026 um 10:17 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.8 (11.09.2026)(11.09.2026 um 12:30 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.9 (11.09.2026)(11.09.2026 um 14:01 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.155.0-alpha.3.10 (11.09.2026)(11.09.2026 um 17:56 Uhr)

🔧 Programmierung 🕛 vor 1 Jahr 6 Min Lesezeit
0

Synchronizing Collaborative Text Editing with Yjs and WebSockets

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

In this article, I almost do a copy-paste of what's found on the internet.

It's a "reminder" for my personal project

where I analyzed the use of library.





NODEJS SERVER



Install:




CODE
npm install ws yjs y-websocket






create:

start.ts




CODE
import WebSocket from 'ws';
const utils = require("y-websocket/bin/utils")

/**
* create a simple websocket server
*/

const wss = new WebSocket.Server({ port: 1234 })

wss.on('connection', (ws, req) => {

// CONNECT the CLIENT to YJS documents
// use the "room" passed in `req.url` (for example "/yjs-ws-demo")
utils.setupWSConnection(ws, req)

// and that's it.... these following are just logs
console.log('CLIENT::CONNECTED')
ws.on('message', message => console.log('CLIENT::MESSAGE', message))
ws.on('close', () => console.log('CLIENT::DISCONNECTED'))
})






run:




CODE
npx ts-node start.ts






So the y-websocket library takes care of everything,

upon client connection, you just need to pass the websocket to the setupWSConnection method.





STORAGE



But if we shut down the server, all Yjs documents are lost.

To store them, we need to use y-leveldb.



Install:




CODE
npm install y-leveldb






add to

start.ts




CODE
import { LeveldbPersistence } from 'y-leveldb';
import * as Y from 'yjs';

const persistence = new LeveldbPersistence('./storage')

// apply DB updates to the Yjs document
persistence.getYDoc('yjs-ws-demo').then((persistedYdoc) => {
Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc))
ydoc.on('update', (update: Uint8Array) => {
persistence.storeUpdate('yjs-ws-demo', update)
})
})






With each update of the Yjs document, I store it in the DB.

It can be optimized by updating after a certain interval of time.

However, with the "update" event, we will always have a binary file "not in clear text"!

This interesting discussion: system:

It allows distributing replicas of a document

these can be updated autonomously without a central server.

I realized that

implementing a custom storage and managing the clear text data of the "update"

could be a problem.





CLIENT SLATE



I use SLATE because these reflections are due to my current project.

Of course, SLATE has nothing to do with Yjs, it's just to give an example.

Later, I'll give an example with a textarea.



Create a ViteJs project for React and TypeScript




CODE
npm create vite@latest my-project --template react-ts






Install:




CODE
npm install yjs y-websocket @slate-yjs/core slate slate-react 






Replace

App.ts




CODE
import { withYjs, withYHistory, YjsEditor } from '@slate-yjs/core';
import { useEffect, useMemo, useState } from 'react';
import { createEditor, Editor, Transforms } from 'slate';
import { Editable, Slate, withReact } from 'slate-react';
import { WebsocketProvider } from 'y-websocket';
import * as Y from 'yjs';

function App() {

const [isOnline, setIsOnline] = useState(false)
// get the Yjs document
const ydoc = useMemo(() => new Y.Doc(), [])
// get the shared data type
const sharedType = useMemo(() => ydoc.get('content', Y.XmlText), [ydoc])
// create the Slate editor connected to Yjs
const editor = useMemo(() => {
const e = withReact(withYHistory(withYjs(createEditor(), sharedType)))
// Extend normalizeNode to avoid the document being empty
// (at least one node must be there otherwise SLATE gets very angry)
const { normalizeNode } = e
e.normalizeNode = (entry) => {
const [node] = entry
if (!Editor.isEditor(node) || node.children.length > 0) {
return normalizeNode(entry)
}
Transforms.insertNodes(editor, [{ children: [{ text: '' }] }], { at: [0] })
}
return e
}, [sharedType])

// when I have all the nice things I connect to the server with the Y-websocket provider
useEffect(() => {
const provider = new WebsocketProvider('ws://localhost:1234', 'yjs-ws-demo', ydoc);
provider.on('status', ({ status }: { status: string }) =>
setIsOnline(status == 'connected')
)

YjsEditor.connect(editor);

return () => {
YjsEditor.disconnect(editor);
provider.destroy();
};
}, [editor, ydoc]);

// and here is a bit of UI
return <div>

<div style={{ color: isOnline ? 'green' : 'red' }}>
{isOnline ? 'Connected' : 'Disconnected'}
</div>

<Slate
editor={editor}
initialValue={[{ children: [{ text: '' }] }]}>
<Editable
renderElement={({ attributes, children, element }) => <p {...attributes}>{children}</p>}
renderLeaf={({ attributes, children, leaf }) => <span {...attributes}>{children}</span>}
placeholder="Enter some text..."
/>
</Slate>
</div>
}

export default App






WebsocketProvider connects the local Yjs doc to the websocket server with the room "yjs-ws-demo":




CODE
new WebsocketProvider('ws://localhost:1234', 'yjs-ws-demo', ydoc)






So from that moment on, the Yjs document is synchronized with the WS server.

Finally, with withYjs and withYHistory, I connect the editor to Yjs.





CLIENT TEXTAREA



And if I wanted a simple textarea?

I found the solution thanks to raine. Great professional.



Install




CODE
npm install fast-diff






Replace


App.ts




CODE
import diff from 'fast-diff';
import { useEffect, useMemo, useState } from 'react';
import { WebsocketProvider } from 'y-websocket';
import * as Y from 'yjs';

function App() {

// get the Yjs document
const ydoc = useMemo(() => new Y.Doc(), [])
// get the shared data type
const sharedType = useMemo(() => ydoc.get('content', Y.Text), [ydoc])
// connect the shared data to a "reactive" string
const [text, setText] = useText(sharedType)

// on component creation, connect via websocket
useEffect(() => {
const provider = new WebsocketProvider('ws://localhost:1234', 'yjs-ws-demo', ydoc);
return () => {
provider.destroy();
};
}, [sharedType]);

// and here is a bit of UI
return <div>
<textarea style={{ width: '100%' }} rows={5}
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
}

export default App






Next, create the custom hook useText:




CODE
/** A hook to read and set a YText value. */
function useText(ytext: Y.Text) : [string, (text: string) => void] {
// the "reactive" string
const [text, setText] = useState(ytext.toString())
// every time the shared data changes, update the "reactive" string
ytext.observe(() => setText(ytext.toString()))
// when I change the "reactive" string, update the Yjs shared data (only the differences)
const setYText = (textNew: string) => {
const delta = diffToDelta(diff(text, textNew));
ytext.applyDelta(delta);
}
return [text, setYText]
}
/** Convert a fast-diff result to a YJS delta. */
function diffToDelta(diffResult: [number, string][]) {
return diffResult.map(([op, value]) => ({
[diff.INSERT]: { insert: value },
[diff.EQUAL]: { retain: value.length },
[diff.DELETE]: { delete: value.length },
}[op])).filter(Boolean);
}






We could send all the text with each change, but that would be inefficient.

So we use fast-diff to calculate the differences between the current text and the previous one.

And then we transform the differences into a delta object that Yjs understands.

The sending to the server is done by y-websocket without us having to worry about it.






CONCLUSIONS



My impression is that Yjs is fantastic!

But for my personal project, I will implement a server-coordinated system for document management.

In this way, I can manage permissions and data persistence more easily.


Also, I have the feeling that the implementation for SLATE is a bit neglected.

I plan to publish some other reminders of my project!


Bye!



ivano

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
After warning AI is too dangerous, Bill Gates bets a billion on its upside
1 Quelle
Jensen Huang puts Trump on speakerphone onstage to announce robots won’t take over the world
1 Quelle
September Patch Tuesday: 963 CVEs, 2 exploited flaws, 1 message
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Synchronizing Collaborative Text Editing with Yjs and WebSockets

Thematisch verwandte Begriffe: Synchronizing, Collaborative, Text, Editing · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...