Andreas Ludvigsson

Bridging Code and Commerce

Creating a Real-time Chatroom with Laravel 11 and Laravel Reverb


Today, I’ll be guiding you through the process of creating a real-time chatroom application using the latest version of the Laravel framework, Laravel 11, and the powerful Laravel Reverb package.

Laravel 11 offers a powerful solution in the form of Laravel Reverb. This innovative package brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, seamlessly integrating with Laravel’s existing suite of event broadcasting tools. By leveraging the power of WebSockets and the flexibility of the Axios library for efficient data fetching and handling, we’ll create a real-time chatroom that delivers an exceptional user experience.

Throughout this article, I’ll walk you through the step-by-step process of setting up the project, configuring the necessary components, and implementing the core functionality of the chatroom. By the end of this journey, you’ll have a solid understanding of how to build a real-time chatroom application that keeps your users engaged and connected in real-time.

Getting Started – Installing Laravel & Laravel Reverb

I use Laravel Herd and create my application through it, but you can easily do it with composer.

To get started, let’s first install Laravel 11 using Composer, the dependency manager for PHP. Open your terminal and run the following command:

composer global require laravel/installer laravel new my-chatroom-app

This will create a new Laravel 11 project named “my-chatroom-app” in your current directory.

Next, we’ll install the Laravel Reverb package. You may install Reverb using the install:broadcasting Artisan command:

php artisan install:broadcasting

This command will install and configure the necessary components for Reverb, ensuring a seamless integration with your Laravel 11 application.

Setting up the Frontend

Once you have installed the above, lets create a sample frontend to get started. In this project, we will use Tailwind for styling, and Axios to send the requests. We will also be using Laravel Vite for asset bundling,

Install Tailwind in Laravel 11

To install Tailwind CSS in your Laravel 11 project, follow these steps:

  1. Open your terminal or command prompt and navigate to your Laravel project directory.
  2. Run the following command to install Tailwind CSS and its peer dependencies:
    npm install -D tailwindcss postcss autoprefixer
  3. Next, run the following command to generate the tailwind.config.js and postcss.config.js files:
    npx tailwindcss init -p

2. Configure your template paths

Open the tailwind.config.js file and add the paths to all of your template files:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./resources/**/*.blade.php",
    "./resources/**/*.js",
    "./resources/**/*.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

3. Add the Tailwind directives to your CSS

Open the ./resources/css/app.css file (or the CSS file you’re using in your Laravel project) and add the following Tailwind directives:

@tailwind base;
@tailwind components;
@tailwind utilities;

That’s it! You’ve now set up Tailwind CSS in your Laravel 11 project. You can start using Tailwind’s utility classes in your Blade templates and start building your Laravel application with the power of Tailwind CSS.

Setting up Controller, Route, Blade, and Channel

1. Creating the Controller

First, we need to create a controller that will handle the storage of messages. This controller will receive messages from the user and dispatch an event. Create a file named ChatController.php in the App\Http\Controllers directory and add the following code:

<?php

namespace App\Http\Controllers;

use App\Events\MessageSent;
use Illuminate\Http\Request;

class ChatController extends Controller
{
    public function store(Request $request)
    {
        $message = $request->input('message');

        MessageSent::dispatch($message);
        return response()->json(['success' => true, 'message' => 'Message sent successfully']);
    }
}

2. Defining Routes

Next, define the routes for displaying the chat view and for the message sending action. Open the web.php file in the routes directory and add the following:

use App\Http\Controllers\ChatController;

Route::get('/', function () {
    return view('chat');
});

Route::post('/chat/send-message', [ChatController::class, 'store'])->name('chat.send');

3. Creating the Blade View

Create a new Blade file named chat.blade.php in the resources/views directory. This file will contain the HTML structure of your chat application. Add the following code:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chatroom</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])


</head>
<body>
<div id="app" class="p-6">
    <div id="chatroom" class="max-w-md mx-auto bg-white shadow-lg rounded-lg overflow-hidden">
        <div class="p-4 bg-gray-200">
            <div class="text-lg font-semibold">Chatroom</div>
        </div>
        <div class="p-4">
            <div id="messages" class="h-64 overflow-y-scroll">
                <div class="bg-gray-100 text-gray-800 p-3 rounded-lg m-2 self-start">
                    <p>This is a message from another user.</p>
                    <div class="text-right text-sm text-gray-500">User Name, 10:00 AM</div>
                </div>
            </div>
            <div class="mt-4">
                <input type="text" id="messageInput" class="w-full p-2 border rounded" placeholder="Type your message here...">
                <button onclick="sendMessage()" class="mt-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700 transition ease-in-out duration-150">Send Message</button>
            </div>
        </div>
    </div>
</div>

</body>
</html>

This view uses Tailwind CSS for styling and includes Laravel Vite for asset compilation. Ensure you have these dependencies configured in your Laravel project.

4. Setting Up the Channel

Finally, define a broadcasting channel that the application will use to broadcast events. Open the channels.php file in the routes directory and add the following:

Broadcast::channel('chatroom', function () {
    return true;
});

This code snippet defines a channel named chatroom that will be used for broadcasting chat messages.

Creating the Event

For real-time communication in a chat application built with Laravel, events play a crucial role. These events are responsible for broadcasting messages to all clients connected to a specific channel. In this part of the tutorial, we will create an event that broadcasts chat messages in real-time.

Creating the MessageSent Event

To handle the broadcasting of messages, we will create an event called MessageSent. This event will implement Laravel’s ShouldBroadcastNow interface, which allows for immediate broadcasting over websockets without queueing. Follow the steps below to create and set up the event:

  1. First, 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 Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

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

    public $message;

    /**
     * Create a new event instance.
     */
    public function __construct($message)
    {
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array
     */
    public function broadcastOn(): array
    {
        return [
            new Channel('chatroom'),
        ];
    }

    public function broadcastAs(): string
    {
        return 'message.sent';
    }
}

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 trait 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 that listeners on the frontend will use to catch this event.

With the MessageSent event created, your Laravel application is now capable of broadcasting messages in real-time. This will enable your chat application to provide a dynamic and interactive user experience.

Set up JavaScript for our Frontend

Creating a seamless user experience in a chatroom requires real-time interactions. To facilitate this, we incorporate JavaScript to handle message sending and receiving dynamically. In this section, we’ll walk through setting up our JavaScript files to make our chatroom lively and interactive.

Creating the chat-scripts.js

To keep our JavaScript organized and maintainable, we create a separate file named chat-scripts.js. This file will contain all the necessary code for our chat functionalities. Here’s what we’ll add to it:

  • Real-time Listening with Laravel Echo: We use Laravel Echo to listen for message events. Whenever a message.sent event is broadcasted, we catch this event in real time and update our chatroom’s UI to display the new message.
  • Sending Messages: We include a function to handle message sending. When a user types a message and hits the send button, this function captures the message, sends it to the server using Axios for an asynchronous HTTP request, and then clears the input field, readying it for the next message.

Here’s the code for chat-scripts.js:

// Listen for new messages using Laravel Echo
window.Echo.channel('chatroom')
    .listen('.message.sent', (e) => {
        console.log(e);
        // Append the new message to the chatroom
        const messages = document.getElementById('messages');
        const messageElement = document.createElement('p');
        messageElement.innerText = e.message;
        messages.appendChild(messageElement);
    });

// Function to send a new message
window.sendMessage = function() {
    const messageInput = document.getElementById('messageInput');
    const message = messageInput.value;
    axios.post('/chat/send-message', { message: message })
        .then(response => {
            console.log(response.data);
            // Clear the input field after sending
            messageInput.value = '';
        })
        .catch(error => console.error(error));

    console.log('sent message');
};

Integrating chat-scripts.js into Our Application

After creating chat-scripts.js, we need to ensure it’s integrated into our Laravel application. We do this by including it in our main JavaScript file, app.js. By importing chat-scripts.js into app.js, we ensure that our chat functionalities are bundled and compiled by Laravel Vite, making them available to our application.

In app.js, simply add the following import statement:

import './bootstrap';
import './chat-scripts';

This setup allows us to maintain a clean code structure while ensuring that all our JavaScript functionalities are properly loaded and executed. By separating our chat-related JavaScript into chat-scripts.js, we also make our code more modular and easier to maintain or extend in the future.

With chat-scripts.js properly set up and included in our application, our chatroom is now equipped with real-time messaging capabilities, enhancing the user experience and making our chatroom application truly interactive.

Firing Up Your Laravel Chat Application with Vite

With the chat application’s backend and frontend 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 troubleshoot any issues that arise during development.

Compiling Assets with Laravel Vite

Laravel Vite is a modern, fast front-end builder 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:

npm run dev

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. 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 form provided. If everything is set up correctly, you should see the message appear in real time—both for you and any other connected clients—without needing to refresh the page. This demonstrates the power of Laravel’s event broadcasting and Vite’s fast development cycle.

Congratulations! You’ve now got a real-time chat application running locally. This setup allows for immediate feedback and iteration, which is invaluable during development.

4 responses to “Creating a Real-time Chatroom with Laravel 11 and Laravel Reverb”

  1. Lyubomir Avatar
    Lyubomir

    But I get an error when I press the button and no message is sent.

    sent message
    chat-scripts.js?t=1711199298653:16

    POST http://localhost:8000/chat/send-message 500 (Internal Server Error)
    dispatchXhrRequest @ axios.js?v=a8e8a14a:1513
    xhr @ axios.js?v=a8e8a14a:1373
    dispatchRequest @ axios.js?v=a8e8a14a:1590
    _request @ axios.js?v=a8e8a14a:1872
    request @ axios.js?v=a8e8a14a:1776
    httpMethod @ axios.js?v=a8e8a14a:1901
    wrap @ axios.js?v=a8e8a14a:8
    window.sendMessage @ chat-scripts.js?t=1711199298653:16
    onclick @ (index):25
    Show 7 more frames
    Show less
    chat-scripts.js?t=1711199298653:22 AxiosError {message: ‘Request failed with status code 500’, name: ‘AxiosError’, code: ‘ERR_BAD_RESPONSE’, config: {…}, request: XMLHttpRequest, …}
    (anonymous) @ chat-scripts.js?t=1711199298653:22
    Promise.catch (async)
    window.sendMessage @ chat-scripts.js?t=1711199298653:22
    onclick @ (index):25
    chat-scripts.js?t=1711199298653:24 sent message
    chat-scripts.js?t=1711199298653:16

    POST http://localhost:8000/chat/send-message 500 (Internal Server Error)
    dispatchXhrRequest @ axios.js?v=a8e8a14a:1513
    xhr @ axios.js?v=a8e8a14a:1373
    dispatchRequest @ axios.js?v=a8e8a14a:1590
    _request @ axios.js?v=a8e8a14a:1872
    request @ axios.js?v=a8e8a14a:1776
    httpMethod @ axios.js?v=a8e8a14a:1901
    wrap @ axios.js?v=a8e8a14a:8
    window.sendMessage @ chat-scripts.js?t=1711199298653:16
    onclick @ (index):25
    Show 7 more frames
    Show less
    chat-scripts.js?t=1711199298653:22 AxiosError {message: ‘Request failed with status code 500’, name: ‘AxiosError’, code: ‘ERR_BAD_RESPONSE’, config: {…}, request: XMLHttpRequest, …}code: “ERR_BAD_RESPONSE”config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}adapter: (2) [‘xhr’, ‘http’]data: “{\”message\”:\”gfhgfhgfhgf\”}”env: {FormData: ƒ, Blob: ƒ}headers: AxiosHeaders {Accept: ‘application/json, text/plain, */*’, Content-Type: ‘application/json’, X-Requested-With: ‘XMLHttpRequest’, X-XSRF-TOKEN: ‘eyJpdiI6IjJpRnRWMXFWcHlXOVVCODc4dmhYWWc9PSIsInZhbH…iYzE5ODVmODY4NTg4NzVkNGU1OTRkODQ3IiwidGFnIjoiIn0=’}maxBodyLength: -1maxContentLength: -1method: “post”timeout: 0transformRequest: [ƒ]transformResponse: [ƒ]transitional: {silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false}url: “/chat/send-message”validateStatus: ƒ validateStatus(status)xsrfCookieName: “XSRF-TOKEN”xsrfHeaderName: “X-XSRF-TOKEN”[[Prototype]]: Objectmessage: “Request failed with status code 500″name: “AxiosError”request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}response: {data: {…}, status: 500, statusText: ‘Internal Server Error’, headers: AxiosHeaders, config: {…}, …}stack: “AxiosError: Request failed with status code 500\n at settle (http://127.0.0.1:5174/node_modules/.vite/deps/axios.js?v=a8e8a14a:1203:12)\n at XMLHttpRequest.onloadend (http://127.0.0.1:5174/node_modules/.vite/deps/axios.js?v=a8e8a14a:1420:7)\n at Axios.request (http://127.0.0.1:5174/node_modules/.vite/deps/axios.js?v=a8e8a14a:1780:41)”[[Prototype]]: Error
    (anonymous) @ chat-scripts.js?t=1711199298653:22
    Promise.catch (async)
    window.sendMessage @ chat-scripts.js?t=1711199298653:22
    onclick @ (index):25
    chat-scripts.js?t=1711199298653:24 sent message

    1. Andreas Avatar

      Hey! Seems to be an error in the controller or the route.. Can you copy the controller code here? 🙂

  2. Carl Avatar
    Carl

    How to do this with Laravel as API and vue 3 as frontend? Been at this for hours… its either authentication errors because i cant get the CSRF cookie or its only update for the sender not for the receiver

    1. Andreas Avatar

      Hey Carl! Do you use Axios? Laravel should apply the CORS (CSRF cookie) automagically

Leave a Reply

Your email address will not be published. Required fields are marked *