Adding Real Time Chat to Laravel Using Reverb & Vue

Last updated on by

Laravel, the web artisan's favorite PHP framework, has just got a whole new powerful tool in its arsenal: Reverb. Among the official packages of Laravel, this WebSocket server application would seamlessly let you integrate real-time features in your Laravel-based applications, thereby taking interaction to a whole new level.

What is Laravel Reverb?

Reverb acts as a mediator between your Laravel-based application and its users. It establishes two-way, real-time communication based on WebSockets technology that allows web pages to receive updates on the server without a complete page refresh. This means that your users experience your application more dynamically and responsively.

Key Features of Laravel Reverb

Blazing Speed: Provides outstanding performance for real-time information with no lags.

Scalability: Grow with your applications to handle increased user traffic.

Seamless Integration: It works with broadcasting features added to Laravel and Laravel Echo to make development simple.

Push Updates: Push updates, messages, or events to clients to share your information instantly.

Built-in Security: Data encryption and authentication assurance for security communication

Adding Laravel Reverb to Your Chat Project

With Laravel Reverb, you can build dynamic chat applications. The messages are posted instantly, making users involved comprehensively. Here's a breakdown of the steps involved:

Step 1: Setting Up Your Laravel Project:

  • Ensure you have a Laravel application set up (version 11 or above is recommended).

  • If you're starting fresh, use composer create-project laravel/laravel your-chat-app-name.

Step 2: Install and Configure Reverb:

Install Laravel Reverb by running the following command:

php artisan install:broadcasting

Once you’ve installed Reverb, you can now modify its configuration from the config/reverb.php file. To establish a connection to Reverb, a set of Reverb “application” credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You can define these credentials using the following environment variables:

BROADCAST_DRIVER=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret

It also automatically creates echo.js in the resources/js directory.

import Echo from 'laravel-echo';
 
import Pusher from 'pusher-js';
window.Pusher = Pusher;
 
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});

Follow the Laravel documentation for configuration steps specific to your application server https://laravel.com/docs/11.x/reverb

Step 3: Running a Server

You can launch the Reverb server by using the reverb:start Artisan command:

php artisan reverb:start

By default, the Reverb server will be started at 0.0.0.0:8080, which makes it accessible from all network interfaces.

If you want to set a specific host or port, you can use the –host and –port options when starting the server.

php artisan reverb:start --host=127.0.0.1 --port=9000

You can also define REVERB_SERVER_HOST and REVERB_SERVER_PORT environment variables in your application’s .env configuration file.

Step 4: Setup Database

Open your .env file and adjust the settings to set up your database. Here’s an example using SQLite for simplicity:

DB_CONNECTION=sqlite
DB_DATABASE=/path/to/database.sqlite

You can create an SQLite database by simply running:

touch /path/to/database.sqlite

For this demo, we’ll create five predefined rooms. Let’s start by generating a model ChatMessage with migration for a chat_messages table.

php artisan make:model ChatMessage --migration

To make it simpler, only create name attributes for this model and migrate it.

Schema::create('chat_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('receiver_id');
$table->foreignId('sender_id');
$table->text('text');
$table->timestamps();
});
php artisan migrate

Now, let's add the necessary relationships in the ChatMessage model. Open the ChatMessage.php file in the app/Models directory and update it as follows:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class ChatMessage extends Model
{
use HasFactory;
 
protected $fillable = [
'sender_id',
'receiver_id',
'text'
];
 
public function sender()
{
return $this->belongsTo(User::class, 'sender_id');
}
 
public function receiver()
{
return $this->belongsTo(User::class, 'receiver_id');
}
}

Step 5: Create Event

To handle the broadcasting of messages, we'll create an event called MessageSent. This event will implement Laravel's ShouldBroadcastNow interface, which allows for immediate broadcasting over WebSockets without queuing. Follow these steps to create and set up the event:

  1. Create a new PHP file in the App\Events directory and name it MessageSent.php.

  2. Open the newly created file and add the following code:

<?php
 
namespace App\Events;
 
use App\Models\ChatMessage;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
 
class MessageSent implements ShouldBroadcastNow
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
 
/**
* Create a new event instance.
*/
public function __construct(public ChatMessage $message)
{
//
}
 
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel("chat.{$this->message->receiver_id}"),
];
}
}

Step 6: Create a Private Channel Route

The channels.php file in Laravel applications plays a crucial role in defining broadcast channels used for real-time communication features.

<?php
 
use Illuminate\Support\Facades\Broadcast;
 
Broadcast::channel('chat.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});

This code defines a private channel named chat.{id} using Laravel's Broadcast facade. Private channels restrict access based on user authentication and authorization logic.

Step 7: Defining Routes

1. Chat Room Route:

Route::get('/chat/{friend}', function (User $friend) {
return view('chat', [
'friend' => $friend
]);
})->middleware(['auth'])->name('chat');

This route is responsible for rendering the chat interface. It accepts a dynamic parameter {friend} representing the user's chat partner.

3. Get Chat Messages Route:

Route::get('/messages/{friend}', function (User $friend) {
return ChatMessage::query()
->where(function ($query) use ($friend) {
$query->where('sender_id', auth()->id())
->where('receiver_id', $friend->id);
})
->orWhere(function ($query) use ($friend) {
$query->where('sender_id', $friend->id)
->where('receiver_id', auth()->id());
})
->with(['sender', 'receiver'])
->orderBy('id', 'asc')
->get();
})->middleware(['auth']);

This route retrieves chat messages exchanged between the authenticated user and the specified friend ({friend}). The query ensures it retrieves messages where either the user is the sender or receiver, including both directions of the conversation.

4. Send Chat Message Route:

Route::post('/messages/{friend}', function (User $friend) {
$message = ChatMessage::create([
'sender_id' => auth()->id(),
'receiver_id' => $friend->id,
'text' => request()->input('message')
]);
 
broadcast(new MessageSent($message));
 
return $message;
});

After creating the message, it leverages Laravel's broadcasting functionality using broadcast(new MessageSent($message)). This line broadcasts the newly created message to all connected users through Reverb, enabling real-time chat functionality.

Step 8: Creating the Blade View

To render the chat interface, you'll need to create a Blade view file. Create a new file named chat.blade.php in the resources/views directory and add the following code:

<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ $friend->name }}
</h2>
</x-slot>
 
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<chat-component
:friend="{{ $friend }}"
:current-user="{{ auth()->user() }}"
/>
</div>
</div>
</div>
</div>
</x-app-layout>

The key element here is <chat-component :friend="{{ $friend }}" :current-user="{{ auth()->user() }}" />. This line renders a Vue.js component named chat-component.

Step 8: Create a Chat Component

<template>
<div>
<div class="flex flex-col justify-end h-80">
<div ref="messagesContainer" class="p-4 overflow-y-auto max-h-fit">
<div
v-for="message in messages"
:key="message.id"
class="flex items-center mb-2"
>
<div
v-if="message.sender_id === currentUser.id"
class="p-2 ml-auto text-white bg-blue-500 rounded-lg"
>
{{ message.text }}
</div>
<div v-else class="p-2 mr-auto bg-gray-200 rounded-lg">
{{ message.text }}
</div>
</div>
</div>
</div>
<div class="flex items-center">
<input
type="text"
v-model="newMessage"
@keydown="sendTypingEvent"
@keyup.enter="sendMessage"
placeholder="Type a message..."
class="flex-1 px-2 py-1 border rounded-lg"
/>
<button
@click="sendMessage"
class="px-4 py-1 ml-2 text-white bg-blue-500 rounded-lg"
>
Send
</button>
</div>
<small v-if="isFriendTyping" class="text-gray-700">
{{ friend.name }} is typing...
</small>
</div>
</template>
 
<script setup>
import axios from "axios";
import { nextTick, onMounted, ref, watch } from "vue";
 
const props = defineProps({
friend: {
type: Object,
required: true,
},
currentUser: {
type: Object,
required: true,
},
});
 
const messages = ref([]);
const newMessage = ref("");
const messagesContainer = ref(null);
const isFriendTyping = ref(false);
const isFriendTypingTimer = ref(null);
 
watch(
messages,
() => {
nextTick(() => {
messagesContainer.value.scrollTo({
top: messagesContainer.value.scrollHeight,
behavior: "smooth",
});
});
},
{ deep: true }
);
 
const sendMessage = () => {
if (newMessage.value.trim() !== "") {
axios
.post(`/messages/${props.friend.id}`, {
message: newMessage.value,
})
.then((response) => {
messages.value.push(response.data);
newMessage.value = "";
});
}
};
 
const sendTypingEvent = () => {
Echo.private(`chat.${props.friend.id}`).whisper("typing", {
userID: props.currentUser.id,
});
};
 
onMounted(() => {
axios.get(`/messages/${props.friend.id}`).then((response) => {
console.log(response.data);
messages.value = response.data;
});
 
Echo.private(`chat.${props.currentUser.id}`)
.listen("MessageSent", (response) => {
messages.value.push(response.message);
})
.listenForWhisper("typing", (response) => {
isFriendTyping.value = response.userID === props.friend.id;
 
if (isFriendTypingTimer.value) {
clearTimeout(isFriendTypingTimer.value);
}
 
isFriendTypingTimer.value = setTimeout(() => {
isFriendTyping.value = false;
}, 1000);
});
});
</script>

This Vue.js component manages the chat interface's dynamic behaviour. It displays a scrollable message list, styled differently based on the sender (current user or friend). It provides an input field for composing new messages and a button to send them. It leverages axios for making HTTP requests to fetch initial messages and send new ones.

Real-time functionality is achieved using Laravel Echo:

  1. It listens for broadcasted MessageSent events to update the message list whenever a new message arrives.

  2. It utilizes whispers on private channels to notify the chat partner about the user's typing activity and receive similar notifications from the friend.

Step 9: Run Project

To run the Laravel project, we need to execute the following command:

php artisan serve

For starting front:

npm run dev

Run reverb:

php artisan reverb:start

Source Code

You can find the source code for this Laravel Reverb chat implementation in the following GitHub repository: https://github.com/qirolab/laravel-reverb-chat

By utilizing Laravel Reverb, developers can ensure their applications remain responsive and dynamic, providing real-time updates and interactions that modern users expect. Happy coding!

Harish Kumar photo

Hey, I'm Harish, a full-stack web developer. I teach web development tutorials with practical screencasts on Qirolab Youtube Channel.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
A Resize Plugin for Alpine.js image

A Resize Plugin for Alpine.js

Read article
How to Migrate MySQL from DBngin to Laravel Herd image

How to Migrate MySQL from DBngin to Laravel Herd

Read article
Learn to master Query Scopes in Laravel image

Learn to master Query Scopes in Laravel

Read article
How to Redirect Uppercase URLs to Lowercase with Laravel Middleware image

How to Redirect Uppercase URLs to Lowercase with Laravel Middleware

Read article
PHP 8.4 Alpha 1 is now out! image

PHP 8.4 Alpha 1 is now out!

Read article
Generics Added to Eloquent Builder in Laravel 11.15 image

Generics Added to Eloquent Builder in Laravel 11.15

Read article