DEV Community

Cover image for 🧠 Build a Real-Time Chat App in Laravel Using Reverb + Blade + Alpine.js
Tahsin Abrar
Tahsin Abrar

Posted on • Edited on

🧠 Build a Real-Time Chat App in Laravel Using Reverb + Blade + Alpine.js

Laravel Reverb is changing the game for real-time applications. If you're looking to build a live chat system without relying on third-party services like Pusher, you’re in the right place.

I’ll guide you through building a real-time public chat app step by step using:

  • Laravel Reverb (WebSocket broadcasting)
  • Laravel Breeze (Blade + Alpine.js)
  • Tailwind CSS
  • Laravel Echo on the frontend

You’ll also learn how to avoid common pitfalls, debug broadcast issues, and get this thing working in a production-friendly way.


🧪 Prefer Cloning? Use This Repository

If you'd like to skip the setup and just see how it's done, feel free to clone the full project from GitHub:

🔗 GitHub Repository
👉 https://github.com/Tahsin000/reverb-live-chat

git clone https://github.com/Tahsin000/reverb-live-chat.git cd reverb-live-chat composer install npm install && npm run dev cp .env.example .env php artisan key:generate php artisan migrate php artisan reverb:start php artisan serve 
Enter fullscreen mode Exit fullscreen mode

Now open http://localhost:8000/chat — and you're in!


🎯 What You’ll Build

A public real-time chat app where:

  • Users send/receive messages instantly
  • Everything updates live with WebSocket (Reverb)
  • The UI is clean and responsive with Alpine.js + Tailwind

🔧 Manual Setup (If You’re Building It Yourself)

Step 1: Create New Project & Install Breeze

laravel new reverb-live-chat cd reverb-live-chat composer require laravel/breeze --dev php artisan breeze:install npm install && npm run dev php artisan migrate php artisan serve 
Enter fullscreen mode Exit fullscreen mode

Step 2: Install and Start Laravel Reverb

php artisan install:reverb php artisan reverb:start 
Enter fullscreen mode Exit fullscreen mode

Update .env:

BROADCAST_DRIVER=reverb 
Enter fullscreen mode Exit fullscreen mode

Step 3: Create Message Model + Migration

php artisan make:model Message -m 
Enter fullscreen mode Exit fullscreen mode

Update migration:

$table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->string('content'); $table->string('room')->default('public'); 
Enter fullscreen mode Exit fullscreen mode

Run:

php artisan migrate 
Enter fullscreen mode Exit fullscreen mode

In Message.php:

protected $fillable = ['user_id', 'content', 'room']; public function user() { return $this->belongsTo(User::class); } 
Enter fullscreen mode Exit fullscreen mode

Step 4: Create and Broadcast Event

php artisan make:event MessageSent 
Enter fullscreen mode Exit fullscreen mode

In MessageSent.php:

class MessageSent implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public function __construct(public Message $message) {} public function broadcastOn(): Channel { return new Channel('chat.public'); } public function broadcastWith(): array { return [ 'message' => $this->message->load('user'), ]; } } 
Enter fullscreen mode Exit fullscreen mode

Add logging if needed:

Log::info('MessageSent Event fired', ['message' => $this->message]); 
Enter fullscreen mode Exit fullscreen mode

Step 5: ChatController

php artisan make:controller ChatController 
Enter fullscreen mode Exit fullscreen mode

Add methods:

public function fetch($room = 'public') { return Message::with('user')->where('room', $room)->latest()->get(); } public function send(Request $request) { $message = Message::create([ 'user_id' => auth()->id(), 'content' => $request->content, 'room' => $request->room ?? 'public', ]); broadcast(new MessageSent($message))->toOthers(); return response()->json(['status' => 'Message Sent!']); } 
Enter fullscreen mode Exit fullscreen mode

Step 6: Routes

Route::middleware('auth')->group(function () { Route::view('/chat', 'chat'); Route::get('/chat/messages', [ChatController::class, 'fetch']); Route::post('/chat/send', [ChatController::class, 'send']); }); 
Enter fullscreen mode Exit fullscreen mode

Step 7: chat.blade.php

@extends('layouts.app') @section('content') <div class="container mx-auto mt-10" x-data="chatApp()" x-init="init()"> <h2 class="text-xl font-bold mb-4">Live Chat</h2> <div class="border p-4 h-96 overflow-y-scroll mb-4 bg-gray-100 rounded"> <template x-for="msg in messages" :key="msg.id"> <div class="mb-2"> <strong x-text="msg.user.name"></strong>: <span x-text="msg.content"></span> </div> </template> </div> <form @submit.prevent="sendMessage" class="flex gap-2"> <input type="text" x-model="newMessage" class="flex-1 border p-2 rounded" placeholder="Type your message..." required> <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Send</button> </form> </div> @endsection @push('scripts') <script src="//unpkg.com/alpinejs" defer></script> <script type="module"> import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: 'local', wsHost: window.location.hostname, wsPort: 6001, forceTLS: false, disableStats: true, }); function chatApp() { return { messages: [], newMessage: '', user: @json(auth()->user()), init() { this.fetchMessages(); Echo.channel('chat.public') .listen('MessageSent', (e) => { this.messages.unshift(e.message); }); }, fetchMessages() { fetch('/chat/messages') .then(res => res.json()) .then(data => this.messages = data); }, sendMessage() { fetch('/chat/send', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ content: this.newMessage }) }).then(() => this.newMessage = ''); } } } </script> @endpush 
Enter fullscreen mode Exit fullscreen mode

🧠 Common Issues and Fixes

Issue Solution
Uncaught ReferenceError: require is not defined Use import instead of require() when working with Vite
Event not firing Check if you ran php artisan queue:work
Queue not logging Add Log::info() inside the event
$slot undefined Switch to @yield('content') in layout
Messages not showing for others Make sure to use .toOthers() when broadcasting

✅ Wrapping Up

You now have a working real-time public chat system in Laravel using native tools — no Pusher needed.

🛠 Want to learn more?
Let me know if you want:

  • Private messaging
  • Typing indicators
  • Group chat rooms

📌 GitHub Repo Again

🔗 Clone it now
👉 https://github.com/Tahsin000/reverb-live-chat

Top comments (0)