Real-time Collaborative Editing in Laravel with Livewire, Turbo Streams, and Yjs

Mahmut Sarıkaya 4 dk okuma 5 Görüntülenme 0
Real-time Collaborative Editing in Laravel with Livewire, Turbo Streams, and Yjs

Can a single Laravel codebase power a Google‑Docs‑like experience?

Developers often face the dilemma of choosing between a robust backend framework and the need for instantaneous, conflict‑free editing. Laravel excels at server‑side logic, while modern front‑end tools such as Turbo Streams and Yjs handle real‑time synchronization. Marrying these three technologies creates a collaborative editor that feels native, scales to dozens of users, and stays within the familiar Laravel ecosystem.

Why Laravel Livewire is the glue for real‑time UI

Livewire eliminates the JavaScript boilerplate by turning PHP classes into reactive components. When a user types, Livewire can instantly broadcast the change to the server, where you can persist it or forward it to other participants. The component below stores the document in the cache and emits an event every time the $content property updates.

<?php
namespace App\\Http\\Livewire;

use Livewire\\Component;
use Illuminate\\Support\\Facades\\Cache;

class CollaborativeEditor extends Component
{
    public $content = '';

    public function mount()
    {
        $this->content = Cache::get('doc_content', '');
    }

    public function updatedContent($value)
    {
        Cache::put('doc_content', $value);
        $this->emit('contentUpdated', $value);
    }

    public function render()
    {
        return view('livewire.collaborative-editor');
    }
}
?>

The Blade view for this component contains a div with wire:model.defer="content" and a wire:ignore wrapper so that Turbo Streams can manipulate the DOM without Livewire interference.

Turbo Streams: server‑driven DOM patches

Turbo Streams, part of Hotwire, let the server push small HTML fragments that replace or prepend elements. In a collaborative scenario, each user’s edit triggers a broadcast that the server converts into a Turbo Stream message. The following snippet shows a typical Turbo Stream response that updates the editor’s container.

<turbo-stream action="update" target="editor">
  <template>
    <div id="editor">{{ $content }}</div>
  </template>
</turbo-stream>

Because Turbo Streams work over standard HTTP or WebSocket connections, you can reuse Laravel’s broadcasting system (Laravel Echo, Pusher, or a self‑hosted Soketi server) without adding a separate real‑time layer.

Yjs provides conflict‑free replicated data types (CRDT)

While Livewire and Turbo Streams handle UI updates, they do not resolve concurrent edits. Yjs fills that gap with CRDTs that guarantee eventual consistency without a central lock. By creating a shared Y.Text instance, every client can apply inserts and deletions locally, while Yjs merges changes from peers in the background.

import * as Y from 'yjs';
import { WebrtcProvider } from 'y-webrtc';
import { Turbo } from '@hotwired/turbo';

const ydoc = new Y.Doc();
const provider = new WebrtcProvider('my-room', ydoc);
const yText = ydoc.getText('shared');

yText.observe(event => {
  Turbo.renderStreamMessage(event.changes.delta);
});

document.getElementById('editor').addEventListener('input', e => {
  yText.delete(0, yText.length);
  yText.insert(0, e.target.innerHTML);
});

The WebrtcProvider creates a peer‑to‑peer mesh, meaning you can run the demo locally without a dedicated signaling server. For production, replace it with y-socket.io or a custom provider that ties into Laravel Echo.

Step‑by‑step: building a minimal collaborative editor

1. System requirements: PHP 8.2+, Composer, Node 18+, Laravel 10, Livewire 2.x, Hotwire Turbo, Yjs 13+. 2. Install packages:

composer require livewire/livewire
npm install @hotwired/turbo yjs y-webrtc

3. Create the Livewire component shown above and publish its Blade view:

php artisan make:livewire collaborative-editor

4. Define a broadcast channel in routes/channels.php that authorizes users for doc.{id}. 5. Wire the component to Turbo by adding @livewireScripts and @livewireStyles to your layout, then include the Turbo Stream listener in resources/js/app.js:

import { Turbo } from '@hotwired/turbo';
Livewire.on('contentUpdated', content => {
  Turbo.renderStreamMessage(``);
});

6. Run the stack with php artisan serve and npm run dev. Open the same URL in two browsers; typing in one instantly reflects in the other, thanks to Yjs merging and Turbo Streams delivering the DOM patch.

Performance tips and common pitfalls

• Cache the document in Redis instead of file‑based cache for sub‑millisecond reads when the user base exceeds 50 concurrent editors. • Limit the size of the Y.Text delta sent over Turbo Streams; truncate updates to 1 KB chunks to avoid hitting the default 2 KB payload limit of many WebSocket providers. • Disable Livewire’s automatic polling (wire:poll) for this page; the CRDT already pushes changes, and extra polling adds unnecessary load.

Conclusion

By leveraging Laravel Livewire’s server‑side reactivity, Turbo Streams’ lightweight DOM patches, and Yjs’s robust CRDT engine, you can deliver a real‑time collaborative editor without abandoning the comfort of a single Laravel codebase. The stack scales from a quick prototype to a production‑grade solution, and the clear separation of concerns keeps each technology in its sweet spot.

Sources

Laravel Livewire Documentation, Hotwire Turbo Streams Guide, Yjs Official Documentation

Author: Mahmut Sarıkaya — sarikayadev.com

Etiketler: #laravel livewire #turbo streams #yjs #real-time collaboration #collaborative editor
Paylaş:
M

Yazar

Mahmut Sarıkaya

yazılım Geliştirici

Yorumlar

Henüz yorum yok. İlk yorumu siz yapın!

Yorum Bırakın

3 + 1 =