Building Real-Time Chat Applications with Laravel Reverb and Vue 3
Real-time chat applications are increasingly important for seamless communication and instant connectivity. Laravel Reverb and Vue.js 3 provide developers with the tools to create efficient and feature-rich chat applications.
👉 Don't left behind, get the Spec Coder VSCode Extension now 🔥
Understanding Laravel Reverb: The Key to Real-Time Communication
Laravel Reverb, introduced in Laravel 11, has changed how developers handle real-time communication in their web applications. This built-in WebSocket server, fully integrated into the Laravel ecosystem, allows developers to create two-way, real-time channels between clients and servers, enabling instant data exchange and updates.
The Power of WebSockets
At the heart of Reverb's functionality is the WebSocket protocol, a technology that enables continuous, two-way communication over a single TCP connection. Unlike traditional HTTP requests, which need constant polling or long-polling techniques, WebSockets allow for easy, real-time data transfer in both directions. This greatly reduces latency and improves overall application performance.
Reverb's Unique Advantages
Reverb stands out from traditional third-party solutions because it is self-hosted, giving developers full control over their real-time infrastructure. As a first-party Laravel package, Reverb integrates seamlessly with the framework's existing event broadcasting system, providing a consistent and familiar development experience.
One of Reverb's most impressive features is its speed and scalability. Written in PHP, the package is optimized for fast communication, efficiently handling thousands of concurrent connections. Additionally, its horizontal scalability allows for easy distribution of connections and data across multiple servers, ensuring your application can grow and adapt to increasing demands.
Harnessing the Power of Vue.js for Real-Time Interactivity
While Laravel Reverb provides the foundation for real-time communication, Vue.js, a progressive JavaScript framework, offers a strong and easy-to-use solution for building dynamic, reactive user interfaces. With its lightweight and modular design, Vue.js allows developers to create highly interactive and responsive applications, making it perfect for real-time chat applications.
Real-Time Updates with Vue.js
One of the main benefits of using Vue.js with Laravel Reverb is its ability to handle real-time updates smoothly. By using Vue.js's reactive data binding and virtual DOM features, developers can easily update the user interface in response to real-time events, like incoming messages or user status changes, without needing to manually manipulate the DOM or refresh the page.
Setting the Stage: Installing Laravel and Laravel Reverb
Before delving into the intricacies of building a real-time chat application, it's essential to set up the development environment. This process involves installing Laravel 11 and the Laravel Reverb package, ensuring a solid foundation for your project.
Installing Laravel 11
Laravel 11 can be installed using Composer, the dependency manager for PHP. Open your terminal or command prompt and execute the following command:
composer global require laravel/installer
laravel new laravel-reverb-chat
This command will create a new Laravel 11 project named laravel-reverb-chat
in your current directory.
Integrating Laravel Reverb
With Laravel installed, you can proceed to integrate the Laravel Reverb package. This can be accomplished using the install:broadcasting
Artisan command:
php artisan install:broadcasting
This command will install and configure the necessary components for Reverb, ensuring seamless integration with your Laravel 11 application.
Crafting the Frontend: Tailwind CSS and Vue 3
To create an engaging and visually appealing chat application, it's important to utilize modern front-end technologies. In this guide, we'll look at how to integrate Tailwind CSS, a utility-first CSS framework, and Vue 3.
There are several packages available for this purpose, such as Laravel-Themer and Laravel Breeze. You can choose any of these options, or alternatively, install Tailwind CSS and Vue 3 manually.
For the purpose of this tutorial, we'll use the Laravel-Themer package, which will automatically set up Tailwind CSS and Vue.js for us, and also supports multi-theming for Laravel applications.
To install the Laravel-Themer package, open the terminal and execute the following command:
composer require qirolab/laravel-themer
Next, to create a theme, run the following command in the terminal and follow the instructions:
php artisan make:theme
You will need to make a few additional configurations, which you can find in the documentation of the Laravel-Themer package. It will also add the login and register features.
Orchestrating the Backend: Models, Controllers, Routes, and Blade Views
With the development environment ready and the frontend technologies in place, it's time to focus on the backend structure of your real-time chat application. This section will guide you through creating controllers, defining routes, and crafting Blade views to manage user interactions and data flow.
Creating the ChatMessage Model & Migration
To start building the backend for our real-time chat application, we need to create a model to represent the chat messages in our database. This model will help us interact with the messages table, making it easier to store, retrieve, and manage chat data.
First, let's generate the ChatMessage
model using the Artisan command-line tool. Open your terminal and run the following command:
php artisan make:model ChatMessage -m
This command will create a new model file named ChatMessage.php
in the app/Models
directory and a migration file in the database/migrations
directory. The -m
flag ensures that a migration file is created along with the model, which we will use to define the structure of the messages table.
Next, open the migration file that was created. It will be located in the database/migrations
directory and will have a name similar to 2023_10_10_000000_create_chat_messages_table.php
. Update the up
method to define the columns for the messages table:
public function up()
{
Schema::create('chat_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('receiver_id');
$table->foreignId('sender_id');
$table->text('text');
$table->timestamps();
});
}
After defining the table structure, run the migration to create the table in your database:
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');
}
}
With the ChatMessage
model and its corresponding table in place, we are now ready to move on to creating controllers, defining routes, and crafting Blade views to handle user interactions and data flow in our real-time chat application.
Defining Routes:
Laravel applications rely on routes to define how incoming HTTP requests are handled. In our real-time chat application built with Reverb and Vue.js 3, we'll define several routes to manage user interactions and message flow. All the routes we define here utilize the auth
middleware, ensuring only authenticated users can access these functionalities.
1. Dashboard Route:
Route::get('/dashboard', function () {
return view('dashboard', [
'users' => User::where('id', '!=', auth()->id())->get()
]);
})->middleware(['auth'])->name('dashboard');
This route handles requests to the /dashboard
URI. The closure function retrieves a list of users excluding the currently authenticated user using User::where('id', '!=', auth()->id())->get()
and then renders the dashboard
view with this user list. The middleware
property applies the auth
middleware, restricting access to authenticated users only. Additionally, the route is named dashboard
for convenient reference elsewhere in the application.
2. 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. Laravel automatically injects a User
instance based on this parameter using route model binding. The closure retrieves the specified user and passes it to the chat
view for display. Similar to the dashboard route, auth
middleware and route naming conventions are applied.
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}
). It utilizes Laravel's query builder to fetch messages from the ChatMessage
model. The query ensures it retrieves messages where either the user is the sender or receiver, including both directions of the conversation. Additionally, it utilizes eager loading (with
) to fetch related sender
and receiver
user information for each message. Finally, messages are ordered chronologically using orderBy('id', 'asc')
.
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;
});
This route handles POST requests to send a new chat message. It accepts the {friend}
parameter similar to the previous route. The route creates a new ChatMessage
instance with the sender being the authenticated user, the receiver being the specified friend, and the message content retrieved from the request body using request()->input('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. Finally, the newly created message is returned as the response.
By defining these routes, we establish a communication channel between users and enable real-time message exchange within our Laravel chat application.
Creating the MessageSent 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:
-
Create a new PHP file in the
App\Events
directory and name itMessageSent.php
. -
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}"),
];
}
}
This event class uses several traits provided by Laravel to handle the broadcasting process efficiently. The Dispatchable
trait allows the event to be dispatched easily, InteractsWithSockets
provides socket interaction capabilities, and SerializesModels
handles model serialization when broadcasting data.
The broadcastOn
method specifies the channels on which the event should be broadcast. In this case, we are broadcasting on the chatroom
channel. The broadcastAs
method defines the name of the event listeners on the front end will use to catch this event.
With the MessageSent
event created, your Laravel application can now broadcast messages in real time, enabling your chat application to provide a dynamic and interactive user experience.
Defining Private Channels in channels.php
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.
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
. The :
symbol denotes passing data to the component.
We pass two variables:
-
:friend
containing the chat partner's information retrieved from$friend
. -
:current-user
containing the currently authenticated user's information obtained usingauth()->user()
.
Chat Component:
This Vue.js component, ChatComponent.vue
, manages the chat interface functionality within the chat.blade.php
view.
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:
-
It listens for broadcasted
MessageSent
events to update the message list whenever a new message arrives. -
It utilizes whispers on private channels to notify the chat partner about the user's typing activity and receive similar notifications from the friend.
<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>
Firing Up Your Laravel Chat Application with Vite
With the chat application's backend and front end now set up, it's time to fire up the application and see the real-time messaging in action. This step involves launching Laravel's real-time event broadcasting server with debugging enabled and utilizing Laravel Vite for a smooth development experience.
Starting Laravel's Real-Time Event Broadcasting Server
To kickstart real-time event broadcasting, you need to start Laravel's event broadcasting server, Reverb. This server will listen for events—like sending a message—and broadcast them to all connected clients in real-time. Open your terminal or command line interface and execute the following command:
php artisan reverb:start --debug
The --debug
flag is crucial as it provides immediate feedback and error logging directly in your console, helping you understand the flow of events and troubleshooting any issues that arise during development.
Compiling Assets with Laravel Vite
Laravel Vite is a modern, fast front-end build tool that enhances your development workflow. It's specially designed to work seamlessly with Laravel, providing hot module replacement (HMR) out of the box. This means you can make changes to your CSS or JavaScript, and see those changes reflected in the browser instantly without a full page reload.
To start Vite and compile your assets, run the following command:
npm run dev
Because we are using the Laravel Themer package, we need to run the following command:
npm run dev:themename
This command boots up Vite, compiles your assets, and watches for any changes you make to your files, reloading parts of the page as needed.
Testing the Chat Application
With the event broadcasting server running and your assets compiled, navigate to your Laravel application's local development URL. Typically, this is, usually, http://localhost:8000
, unless you've configured a different port or are using a tool like Laravel Valet that might assign a different domain. Once there, you should see your chat application's interface. Try sending a message using the provided form. If everything is set up correctly, you should witness the message appearing in real-time—both for you and any other connected clients—without necessitating a page refresh. This remarkable demonstration showcases the prowess of Laravel's event broadcasting in conjunction with Vite's rapid development cycle.
Congratulations! You've now successfully constructed a fully operational, real-time chat application running locally. This meticulously crafted setup facilitates immediate feedback and iteration, an invaluable asset during the development phase.
Conclusion: Embracing the Power of Real-Time Communication
In today's fast-paced digital landscape, real-time communication has become a fundamental aspect of modern web applications. By leveraging the power of Laravel Reverb and Vue.js, developers can create feature-rich, real-time chat applications that foster seamless communication and enhance user engagement.
Throughout this comprehensive guide, we've explored the intricacies of building a real-time chat application, from setting up the development environment and configuring the necessary components to implementing the core functionality and exploring advanced features.
As you embark on your journey to create real-time applications, remember to stay curious, embrace emerging technologies, and prioritize accessibility and inclusive design. The future of web development is ever-evolving, and by staying informed and adapting to new trends, you can ensure that your applications remain relevant, performant, and engaging for years to come.
Embrace the power of real-time communication, and let your applications transcend the boundaries of traditional web development, fostering connections and facilitating seamless interactions in the digital realm.