Laravel 11 Real Time Chat Using Reverb and Vue

Hello, laravel web developers! In this article, we'll see how to create real-time chat in laravel 11 reverb with vue.js. Here, we'll create a real-time chat application in laravel 11. Reverb is a first-party WebSocket server for Laravel applications, bringing real-time communication between client and server directly to your fingertips

It's two-way, real-time communication based on WebSockets technology that allows web pages to receive updates on the server without a complete page refresh.

Laravel 11 Real Time Chat using Reverb and Vue

laravel 11 real time chat using reverb and vue

 

Step 1: Install Laravel 11 Application

In this step, we'll install the laravel 11 application using the following command.

composer create-project laravel/laravel laravel_11_live_chat_example

 

Step 2: Install and Configure Reverb

Next, we'll install Laravel Reverb by running the following command.

php artisan install:broadcasting

This command will run the reverb:install command, which will install Reverb with a sensible set of default configuration options. you can modify its configuration from the config/reverb.php file.

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 will also 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'],
});

 

Step 3: Running a Server

Now, run the reverb server using the following command.

php artisan reverb:start

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=8001

 

 

Step 4: Configure Database

Then, we'll configure a database and add the host, database name, etc. 

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_11
DB_USERNAME=root
DB_PASSWORD=root

Next, we'll create a Model and migration using the following command.

php artisan make:model ChatMessage --migration

Migration:

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

Next, migrate the table into the database using the following command.

php artisan migrate

Now, update the ChatMessage.php Model.

app/Models/ChatMessage.php

<?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

Next, we'll create an event MessageSent to handle the broadcasting of messages. This event will implement Laravel's ShouldBroadcastNow interface, which allows for immediate broadcasting over WebSockets without queuing.

app/Events/MessageSent.php

<?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

Then, we'll create a private channel for real-time communication.

channels.php

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

 

Step 7: Define Routes

Next, we'll define the routes for the chat room, get a chat message, and send a chat message.

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

// 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']);

// 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;
});

 

 

Step 8: Create Blade View

Then, we'll create a new file chat.blade.php in the resources/views directory and add the following code.

resources/views/chat.blade.php

<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>

 

Step 9: Create a Chat Component

Then, we'll create a chat component and add the following code.

<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>

 

Step 10: Run Laravel 11 Application

Now, run the laravel 11 application using the following command.

php artisan serve

Then, run the following command.

npm run dev

Run laravel reverb using the following command.

php artisan reverb:start

 


You might also like:

techsolutionstuff

Techsolutionstuff | The Complete Guide

I'm a software engineer and the founder of techsolutionstuff.com. Hailing from India, I craft articles, tutorials, tricks, and tips to aid developers. Explore Laravel, PHP, MySQL, jQuery, Bootstrap, Node.js, Vue.js, and AngularJS in our tech stack.

RECOMMENDED POSTS

FEATURE POSTS