How to streaming text with markdown formatted in Livewire?

In Laravel Livewire, you can use the wire:stream directive to stream data from the server to the client. This directive allows you to send data in real-time, enabling you to update the UI dynamically without reloading the page.

But wire:stream directive will send the data as a string, not as HTML. So if you want to stream the text with markdown formatted you need to do a little bit of work.

Streaming text in Livewire

Streaming text become a trend since the Large Language Model (LLM) become more popular. The text generation by LLM takes time so rather than waiting for the LLM to finish generating the text, you can stream the text in real-time for better user experience.

Last year I made a blog post about how to streaming text with server sent events (SSE) in Laravel. We can do the streaming text manually using SSE and some javascript code in the client side. But using Livewire we can do it in a much easier way.

So let's learn how to streaming text using Livewire.

  1. First, we need to create a component that will be used to stream the text. We will call it MessageItem.
<?php

namespace App\Http\Livewire;

use Livewire\Component;

class MessageItem extends Component
{
    public $message;

    public function mount(string $message)
    {
        $this->message = $message;
    }
}
  1. Next, we need to create a template for the component. We will call it message-item.blade.php.
<div class="message-item">
    <p>{{ $message }}</p>
</div>
  1. Now, we need to create a Livewire component that will be used to stream the text. We will call it MessageStream.
<?php

namespace App\Http\Livewire;

use Livewire\Component;

class MessageStream extends Component
{
    public $messages = [];

    public function mount()
    {
        $this->messages = [
            'Hello, world!',
            'This is a test message.',
            'How are you doing today?',
        ];
    }

    public function render()
    {
        return view('livewire.message-stream');
    }

    public function stream()
    {
        foreach ($this->messages as $message) {
            yield $message;
        }
    }
}
  1. Now, we need to create a template for the component. We will call it message-stream.blade.php.
<div class="message-stream">
    @foreach ($messages as $message)
        <livewire:message-item :message="$message"></livewire:message-item>
    @endforeach
</div>
  1. Finally, we need to create a route that will be used to stream the text. We will call it stream.
Route::get('/stream', 'MessageStream@stream');

Streaming markdown

Sometimes you need to stream the text and render it as markdown. Unfortunately, the streaming from Livewire is not formatted from the server, and the solution is just like when we stream markdown in React - we render the markdown on the client.

The steps for this are:

  1. Stream the text inside the hidden element.
  2. Listen for HTML changes and render them into a new element with markdown rendered.
  3. Use marked.js to render the markdown HTML, but feel free to use another library you prefer.