Building Real-Time Chat App Using Laravel Reverb and Vue

Create instant messaging features with Laravel's first-party WebSocket server and Vue.js reactive components

Real-time communication has become essential for modern web applications. Whether you're building a customer support chat, team collaboration tool, or social platform, the ability to exchange messages instantly without page refreshes dramatically improves user experience. Laravel Reverb, introduced with Laravel 11, provides a first-party WebSocket server that makes implementing real-time features straightforward and scalable.

This comprehensive guide walks you through building a fully functional real-time chat application using Laravel Reverb and Vue.js. You'll learn how to set up the backend infrastructure, handle message broadcasting, and create a responsive frontend that delivers instant updates to all connected users.

What You'll Learn

By the end of this tutorial, you'll understand how to implement WebSocket communications between your Laravel backend and Vue.js frontend. The application we'll build supports multiple users chatting in real-time, with messages appearing instantly across all connected clients without requiring manual page refreshes.

What You'll Learn

Laravel Reverb Architecture

Understand how Laravel's first-party WebSocket server enables real-time bidirectional communication

Backend Implementation

Set up models, migrations, controllers, and event broadcasting for chat functionality

Frontend Integration

Build reactive Vue.js components that receive real-time updates via WebSocket connections

Production Deployment

Configure SSL/TLS, horizontal scaling, and process management for production environments

Prerequisites and Environment Setup

Before you begin building the chat application, ensure your development environment meets the necessary requirements. Having the right tools installed and configured will make the development process smoother and help you avoid common setup issues.

Required Software and Versions

The tutorial assumes you have PHP 8.2 or later installed, as Laravel 11 requires a modern PHP version. You can verify your PHP version by running php -v in your terminal. Composer must also be installed for managing PHP dependencies. For the frontend, you'll need Node.js version 20 or higher, which provides the npm package manager needed for building Vue.js assets.

A database server is required for storing chat messages. MySQL 5.7 or later works well, though you could also use PostgreSQL or SQLite for development purposes. Make sure your database server is running and that you have created a blank database for your application. The database credentials will be configured in your Laravel application's .env file.

Installing Laravel 11

Begin by creating a new Laravel 11 project using Composer. The Laravel installer or Composer create-project command will set up the project structure, configure dependencies, and prepare the initial files:

composer create-project laravel/laravel:^11.0 chat-app
cd chat-app

After the installation completes, you can verify everything is working by starting the development server:

php artisan serve

Visit http://localhost:8000 in your browser to confirm the Laravel welcome page appears. This confirms that your Laravel installation is functioning correctly and you're ready to proceed with adding Reverb and other dependencies.

Database Model and Migration Design

The chat application needs to store messages persistently so users can see conversation history when they reload the page or join an ongoing discussion. We'll create a Message model and corresponding database migration to handle this data.

Creating the Message Model

The Message model represents a single chat message in your application. Each message belongs to a user (the person who sent it) and contains the text content of the message along with timestamps for creation and potential updates. Laravel's Eloquent ORM makes it straightforward to define these relationships and access patterns.

Generate the model and migration using Artisan:

php artisan make:model Message -m

The -m flag tells Artisan to also create a migration file for the database table. Open the generated model file and define the fillable attributes and relationships:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Message extends Model
{
 protected $fillable = ['user_id', 'text'];
 protected $appends = ['time'];

 public function user(): BelongsTo
 {
 return $this->belongsTo(User::class);
 }

 public function getTimeAttribute(): string
 {
 return date('d M Y, H:i:s', strtotime($this->created_at));
 }
}

The getTimeAttribute accessor provides a human-readable formatted timestamp for display in the chat interface. This follows Laravel's naming conventions for accessors, automatically linking the time attribute to the underlying created_at database column.

Database Migration Structure

The migration file defines the structure of the messages table. Each message needs an auto-incrementing ID, a foreign key referencing the user who sent it, the message text, and timestamps:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
 public function up(): void
 {
 Schema::create('messages', function (Blueprint $table) {
 $table->id();
 $table->foreignId('user_id')->constrained();
 $table->text('text')->nullable();
 $table->timestamps();
 });
 }

 public function down(): void
 {
 Schema::dropIfExists('messages');
 }
};

After creating the migration, configure your database connection in the .env file. Once configured, run the migrations to create the tables:

php artisan migrate

This command creates all necessary tables, including the messages table, the users table from Laravel's authentication scaffolding, and any other tables your application requires.

Introduction to Laravel Reverb

Laravel Reverb represents a significant advancement in the Laravel ecosystem. Before its introduction, Laravel developers who wanted real-time capabilities had to rely on third-party solutions or complex setups involving separate WebSocket servers like Pusher or custom implementations using Node.js. Reverb changes this by providing an official, well-integrated solution that works seamlessly with Laravel's existing features.

What is Laravel Reverb?

Laravel Reverb is a first-party WebSocket server designed specifically for Laravel applications. It enables real-time, bidirectional communication between the server and connected clients. Unlike traditional HTTP requests where the client initiates communication, WebSocket connections remain open, allowing the server to push data to clients immediately when events occur.

Reverb was developed with several key principles in mind. First, it needed to be fast and performant, capable of handling thousands of concurrent connections. Second, it had to integrate smoothly with Laravel's existing broadcasting system, allowing developers to use familiar patterns like event broadcasting and channels. Third, it needed to be horizontally scalable, meaning you can add more server instances as your application grows.

How Reverb Differs from Traditional Approaches

Traditional approaches to real-time communication often involved polling, where the client repeatedly asks the server for new data at regular intervals. This approach is inefficient because it generates unnecessary HTTP requests even when no new data exists, wasting server resources and potentially introducing latency. WebSocket connections, which Reverb enables, solve this problem by maintaining persistent connections that the server can use to push data instantly.

Before Reverb, Laravel developers typically had two main options for real-time functionality. The first was using a service like Pusher, which works well but introduces an external dependency and ongoing costs. The second was setting up a separate WebSocket server using libraries like Ratchet for PHP or Socket.io for Node.js, which added complexity to the infrastructure and required maintaining additional components. Reverb provides a third option: a native Laravel solution that keeps everything within your application's ecosystem.

Key Features of Laravel Reverb

Reverb offers several features that make it an attractive choice for Laravel developers. It provides native support for Laravel's broadcasting system, meaning you can use familiar constructs like Broadcast::channel() and event(new MessageSent($message)) to send data to connected clients. The server supports both public and private channels, enabling you to implement authentication and authorization for your real-time features.

One of Reverb's most significant advantages is its horizontal scalability. When your application grows and a single server can no longer handle the load, you can deploy multiple Reverb instances behind a load balancer. These instances communicate through Redis Pub/Sub, ensuring that messages broadcast on one server reach clients connected to any other server in the cluster.

Installing and Configuring Laravel Reverb

With the backend logic in place, it's time to add Laravel Reverb for real-time capabilities. Reverb integrates with Laravel's existing broadcasting system, making it straightforward to implement.

Installing Reverb

Install Reverb using Composer, which will download the package and set up basic configuration:

composer require laravel/reverb
php artisan reverb:install

The installer creates a configuration file and adds the necessary service provider. It also publishes configuration options that you'll need to review. The main configuration file, typically found at config/reverb.php, contains settings for the WebSocket server host, port, and various behavior options.

Configuration Options

After installation, open the Reverb configuration file. Key settings include the host and port on which the WebSocket server listens, options for TLS/SSL in production environments, and settings for handling connections and message sizes. For local development, the defaults typically work well, but you may need to adjust the host to 0.0.0.0 if accessing from different devices on your network.

The configuration also includes settings for horizontal scaling if you plan to run multiple Reverb instances. When enabled, Reverb uses Redis Pub/Sub to coordinate message distribution across server instances. This is essential for production deployments where multiple servers handle requests.

Start the Reverb server:

php artisan reverb:start

You should see output indicating the server is listening on the configured port. This server handles all WebSocket connections and message routing for your application.

Broadcasting Events

Laravel's broadcasting system integrates with Reverb to send events to connected clients. We'll create an event that fires whenever a new message is created, allowing the frontend to receive and display it instantly.

Creating the MessageSent Event

Generate a new event class that implements the ShouldBroadcast interface:

<?php

namespace App\Events;

use App\Models\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageSent implements ShouldBroadcast
{
 use Dispatchable, InteractsWithSockets, SerializesModels;

 public Message $message;

 public function __construct(Message $message)
 {
 $this->message = $message->load('user:id,name');
 }

 public function broadcastOn(): array
 {
 return [
 new Channel('chat'),
 ];
 }
}

The ShouldBroadcast interface tells Laravel that this event should be sent to the WebSocket server. The broadcastOn method specifies which channels the event should be sent to. For a public chat, the Channel class creates a public channel that any client can subscribe to.

Triggering the Event

Modify the message method in your controller to fire the event after creating the message:

use App\Events\MessageSent;

// In the message method, after creating the message:
broadcast(new MessageSent($message))->toOthers();

return $message->load('user:id,name');

The toOthers() method ensures that the sender doesn't receive their own message back through the WebSocket connection, since they've already seen it appear immediately after sending. This is a common pattern that prevents duplicate messages from appearing in the sender's interface.

Vue.js Frontend Implementation

With the backend ready, we can now build the Vue.js frontend. The frontend handles displaying the chat interface, sending messages, and receiving real-time updates through WebSocket connections. Vue's reactive data binding makes it an excellent choice for real-time applications, as the interface updates automatically whenever new messages arrive.

Setting Up Vue Components

Create a new Vue component for the chat interface. This component manages the message list, input field, and WebSocket connection. Here's a typical implementation structure:

<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import axios from 'axios';

const messages = ref([]);
const newMessage = ref('');
const messagesContainer = ref(null);
const isSending = ref(false);

const fetchMessages = async () => {
 const response = await axios.get('/messages');
 messages.value = response.data;
 scrollToBottom();
};

const sendMessage = async () => {
 if (!newMessage.value.trim() || isSending.value) return;

 isSending.value = true;
 try {
 await axios.post('/message', { text: newMessage.value });
 newMessage.value = '';
 } catch (error) {
 console.error('Failed to send message:', error);
 } finally {
 isSending.value = false;
 }
};

const scrollToBottom = async () => {
 await nextTick();
 if (messagesContainer.value) {
 messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
 }
};

onMounted(() => {
 fetchMessages();

 window.Echo.channel('chat')
 .listen('MessageSent', (event) => {
 messages.value.push(event.message);
 scrollToBottom();
 });
});

onUnmounted(() => {
 window.Echo.leave('chat');
});
</script>

This component uses Laravel Echo, which provides a unified API for consuming WebSocket events. When the component mounts, it fetches existing messages and subscribes to the chat channel. The listen method specifies that when a MessageSent event is received, the message should be added to the local array.

Template Structure

The template uses conditional classes to distinguish between the current user's messages and messages from other users. The scroll-to-bottom functionality ensures that new messages are always visible without requiring the user to scroll manually:

<template>
 <div class="chat-container">
 <div class="messages-list" ref="messagesContainer">
 <div
 v-for="message in messages"
 :key="message.id"
 :class="['message', { 'own-message': message.user_id === currentUserId }]"
 >
 <div class="message-content">{{ message.text }}</div>
 <div class="message-meta">
 {{ message.user.name }} • {{ message.time }}
 </div>
 </div>
 </div>

 <div class="message-input">
 <input
 v-model="newMessage"
 @keyup.enter="sendMessage"
 placeholder="Type a message..."
 :disabled="isSending"
 />
 <button @click="sendMessage" :disabled="isSending">
 Send
 </button>
 </div>
 </div>
</template>

This implementation provides a natural chat experience that matches user expectations from other messaging applications. The reactive nature of Vue.js ensures that the interface updates instantly when new messages arrive through the WebSocket connection. For teams looking to implement similar real-time features in their web applications, our web development services can help architect and build scalable solutions.

Production Deployment Considerations

When deploying your chat application to production, several additional considerations ensure reliable operation and good user experience.

Server Configuration

For production deployments, you'll need a server capable of running both the Laravel application and the Reverb WebSocket server. The WebSocket server requires a persistent process, which means using a process manager like Supervisor to ensure it stays running and restarts automatically if it crashes. Configure your server's firewall to allow traffic on the WebSocket port only from trusted sources if needed.

If you expect high traffic or need redundancy, deploy multiple Reverb instances behind a load balancer. Configure all instances to connect to the same Redis server for message coordination. This setup ensures that users connected to any server receive messages broadcast from any other server, maintaining a unified chat experience across your infrastructure.

SSL/TLS Configuration

WebSocket connections work with SSL/TLS just like HTTP connections, providing encryption for real-time communication. Configure your Reverb server to use WSS (WebSocket Secure) connections in production:

// config/reverb.php
'ssl' => [
 'key' => '/path/to/private.key',
 'cert' => '/path/to/certificate.crt',
],

Using SSL is essential for security and also required for some browser features to function correctly.

Scaling Considerations

As your application grows, monitor the WebSocket server's resource usage and connection counts. Each connected user maintains a persistent connection, which consumes memory on the server. If you approach resource limits, consider scaling horizontally by adding more Reverb instances. The Redis Pub/Sub integration ensures message distribution across all instances, though you may need to adjust Redis configuration for high-throughput scenarios.

For applications requiring enterprise-grade scalability, consider integrating with our custom web application development services to architect solutions that handle millions of concurrent connections with proper load balancing and infrastructure planning.

Extending the Application

The basic chat application provides a foundation that you can extend with additional features to meet specific business requirements.

Possible Extensions

FeatureImplementation Approach
Direct MessagingPrivate channels with user-specific authorization
Read ReceiptsTrack message viewed status in database
Typing IndicatorsBroadcast typing start/stop events
Presence ChannelsShow which users are currently online
Push NotificationsLaravel notifications for offline users
Message ReactionsStore reaction data with relationships

Advanced Features

For more advanced features, explore Laravel's presence channels, which allow you to see which users are currently online in a chat room. This requires additional frontend handling to track user joins and leaves, but provides a richer collaborative experience. You can also integrate with Laravel's notification system to send push notifications when users receive messages while offline.

Consider implementing direct messaging between specific users by using private channels and user-specific authorization. Add message read receipts by tracking when users view messages. Implement typing indicators to show when other users are composing responses.

Next Steps

  • Implement direct messaging between users
  • Add message editing and deletion
  • Create chat rooms with topic-based organization
  • Integrate file/image sharing capabilities
  • Add message search functionality

Building real-time features like chat requires expertise in both backend infrastructure and frontend reactivity. Our team at Digital Thrive specializes in full-stack web development and can help you architect and implement custom real-time solutions tailored to your business needs.

Frequently Asked Questions

Ready to Build Real-Time Features?

Our team specializes in Laravel applications with advanced real-time capabilities. Let's discuss your project requirements and build a solution that scales with your business.