You use realtime communication every day. It is the simultaneous exchange of information between a sender and a receiver with almost zero latency. Internet, landlines, mobile/cell phones, instant messaging (IM), internet relay chat, videoconferencing, teleconferencing, and robotic telepresence are all examples of realtime communication systems.

In this tutorial, you’ll learn how to build a realtime public chat app using React.js, Laravel, and Ably. You’ll use React.js to build the frontend/UI and Laravel to interact with Ably Realtime APIs to facilitate realtime communication. Anyone on the internet would be able to use this app to post messages to a public chat room and talk anonymously with other connected users. By building this kind of application, you’ll learn about the relevant concepts for building applications that need real-time data transfer.
The entire code for the project is available in this GitHub repo.
Architecture of the realtime chat app
Before getting started, you should familiarize yourself with the tech stack that you’ll be using in this tutorial to build your chat application.
React.js
React.js is an open-source JavaScript library that is used for building reactive user interfaces and is often used for building client side rendered single-page applications. It is very easy to integrate it with TypeScript. Based on this tutorial, you can also perform the same set of actions in Next.js, which is a meta framework for developing universal React.js applications that are both client and server side rendered.
Laravel
Laravel is a PHP-based web application framework with expressive, elegant syntax. The web development community loves Laravel because it provides an amazing developer experience and supports advanced concepts, like queues, dependency injection, unit testing, and integration testing. Laravel is a great choice for building realtime apps, and it already comes with support for Ably to "broadcast" your server-side Laravel events over a WebSocket connection.
Ably
Ably is a pub/sub messaging platform that powers synchronized digital experiences in realtime for millions of simultaneously connected devices around the world. Ably offers WebSockets, stream resume, history, presence, and managed third-party integrations to make it simple to build, extend, and deliver digital realtime experiences at scale.
The architecture of the realtime chat app will look this:

Setting up your Ably account
To start, you need to create an Ably account if you don’t have one.
Next, you need to grab the API key from the Ably dashboard:
1. Visit your app dashboard and click Create New App.
2. Copy the private API key once the app has been created. Keep the key safe; you will use it to authenticate with the Ably service in Laravel and React.js.

3. Enable the Pusher protocol support in your Ably app settings to use the Pusher protocol with Ably. You can find this feature in the Protocol Adapter Settings under the Settings tab. In case you’re asked about creating default namespaces, you should click No thanks.

That’s it. Your Ably account is ready to support real-time messaging.
Setting up the environment
Here is what you’ll need to get started.
Prerequisites
- Node.js: This tutorial uses Node v16.14.0
- PHP: This tutorial uses PHP v7.4.3
- Composer: This tutorial uses Composer v2.1.0
Setting Up the Project
You’ll need a main directory that holds the code for both the frontend (React.js) and backend (Laravel). Open your terminal, navigate to a path of your choice, and create a directory chat-app-react-laravel-ably
by running the following command:
mkdir chat-app-react-laravel-ably
In the chat-app-react-laravel-ably
directory, you’ll install both Laravel and React.js projects.
Setting up the backend
Start by building the backend part of the chat app.
Then in your terminal, execute the following command to create a Laravel project in the backend
directory:
composer create-project laravel/laravel backend
Next, once the above command has been executed, start your Laravel project by running the following commands in your terminal:
cd backend
php artisan serve
Finally, open localhost:8000 in your browser, and you’ll see your Laravel project running:

Setting up broadcasting
You’ll need to register the App\Providers\BroadcastServiceProvider
before broadcasting any events. To do so, open the config/app.php
file and then uncomment the BroadcastServiceProvider
in the providers
array:
'providers' => [
...
App\Providers\BroadcastServiceProvider::class,
...
]
This BroadcastServiceProvider
class contains the necessary code for registering the broadcast authorization routes and callbacks.
Next, since you want to broadcast your events using Ably, install the Ably PHP SDK by running the following command in your terminal:
composer require ably/ably-php
You should also configure your Ably credentials in the config/broadcasting.php
file. An example Ably configuration is already included in this file, allowing you to quickly specify your key.
Then add the ABLY_KEY
environment variable to the .env
file and replace your-ably-key
with your registered Ably app’s key from the Ably dashboard:
ABLY_KEY=your-ably-key
Lastly, change the broadcast driver (BROADCAST_DRIVER
) to ably
in the .env
file:
BROADCAST_DRIVER=ably
Your Laravel project is now ready to start broadcasting events.
Broadcasting events in Laravel with WebSockets
Before writing the code for broadcasting events to your React.js frontend, it is important to know how Laravel’s broadcasting works.
Laravel’s event broadcasting allows you to broadcast your server-side events to your client-side application using a driver-based approach to WebSocket. You can consume these events on the client-side using the Laravel Echo npm package.
Events are broadcast over channels. Your application can subscribe to these channels with/without authentication or authorization, depending on whether the channels are public or private.
To send events from Laravel, create an event file by running the following command in your terminal:
php artisan make:event MessageEvent
This command will create a MessageEvent.php
file in the app/Events
directory.
Next, open the app/Events/MessageEvent.php
file and replace its content with the following code:
<?php
namespace App\Events;
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;
use Illuminate\Support\Str;
// 1
class MessageEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
private $user, $message;
// 2
public function __construct($user, $message)
{
$this->user = $user;
$this->message = $message;
}
// 3
public function broadcastWith()
{
return [
'id' => Str::orderedUuid(),
'user' => $this->user,
'message' => $this->message,
'createdAt' => now()->toDateTimeString(),
];
}
// 4
public function broadcastAs()
{
return 'message.new';
}
// 5
public function broadcastOn()
{
return new Channel('public.room');
}
}
Here are the specifics in the above code:
1. You define a MessageEvent
class that implements the `ShouldBroadcast` interface.
2. You define a constructor (__construct
) for the MessageEvent
class to which you’ll pass the user ($user
) and message $message
) variables when creating the MessageEvent
event object.
3. You define a broadcastWith
method that returns the array of data related to the message you want to broadcast. By default, all an event class’s public
properties are automatically serialized and broadcast as the event’s payload. So this method gives you more control over the data you broadcast over the event’s channels.
4. You specify the broadcast name as message.new
by defining a broadcastAs
method. This method is not required because, by default, Laravel broadcasts the event using the event’s class name.
5. You define a broadcastOn
method that is responsible for returning the channels on which the event should broadcast. In this case, the event would be broadcast on a public channel (Channel
) named public.room
. If you want to broadcast an event on a private channel, you use PrivateChannel
instead of Channel
.
Receiving requests from the React.js frontend
In this chat app project, the frontend will send a POST request to the Laravel backend. Upon receiving the request, Laravel will launch the MessageEvent
event and broadcast the event using the Ably-specified broadcast driver.
To receive API requests from the frontend, open the routes/api.php
file and replace its content with the following code:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Events\MessageEvent;
// 1
Route::post('new-message', function (Request $request) {
// 2
event(new MessageEvent($request->user, $request->message));
return 'ok';
});
In the above code, here are the specifics:
1. You define an API route (`new-message`) to which you would be able to send POST requests. This API route will be available at localhost:8000/api/new-message].
2. You fire the MessageEvent
event by passing the user ($request->user
) and message ($request->message
) variables as arguments.
Finally, to check if the code is working correctly so far, try sending a POST request to the localhost:8000/api/new-message endpoint using Postman or any other tool and sending the user
and message
as a payload. You should receive an ok
response. If you do not receive an ok
response, please carefully review the steps of the tutorial to ensure that you have followed them correctly.
That’s it for the Laravel part of the project. Next, you need to set up a React.js app to listen to the events being broadcast by the Laravel backend.
Setting up the React.js frontend
Now that you have completely set up your Laravel project, it’s time to build the React.js frontend app.
Since your current terminal window is serving the Laravel project, open another terminal window and execute the following command from the project’s root directory (chat-app-react-laravel-ably
) to create a React.js project using the famous create-react-app:
npx create-react-app frontend
Finally, once the installation is complete, navigate to the frontend
directory and start the React.js development server by running the following commands in your terminal:
cd frontend
npm start
This command will start the development server on port 3000 and take you to localhost:3000. The first view of the React.js website will look like this:

Installing frontend packages
You will need to install the following packages to create the frontend of the chat app:
- axios: This package allows you to make HTTP requests in your client-side application.
- Laravel Echo: This JavaScript library allows you to subscribe to channels and listen to the events being broadcast by your server-side broadcasting driver.
- pusher-js: You might think “why do I need pusher-js when I am using Ably to broadcast my events?” This is because Ably includes a Pusher protocol adapter that lets you use the Pusher protocol when listening for events in your client-side application.
Shut down the React.js development server by pressing Control-C and then execute the following command to install the required modules for your React.js app:
npm install axios laravel-echo pusher-js
Once the installation is complete, add the REACT_APP_MIX_ABLY_PUBLIC_KEY
environment variable to the .env
file. Your Ably public key is the portion of your Ably key that occurs before the :
character:
REACT_APP_MIX_ABLY_PUBLIC_KEY=<your-ably-public-key>
REACT_APP_API_BASE_URL=http://localhost:8000/api
In the above configuration, you have specified REACT_APP_MIX_ABLY_PUBLIC_KEY
and REACT_APP_API_BASE_URL
. It is important to prefix your environment variables with REACT_APP_
so that they can be used by create-react-app.
Setting up Laravel Echo
You must set up Laravel Echo before it can help you subscribe to channels and listen for events. Open the src/App.js
file and input the following code:
// 1
import PublicMessagesPage from './components/PublicMessagesPage';
// 2
export default function App() {
return <PublicMessagesPage />;
}
In the above code, this is what’s happening:
1. You import the PublicMessagesPage
component.
2. You render the PublicMessagesPage
component in the `App` component.
Building the frontend UI
You will now build the public chat home page and message box component that comprise the app’s frontend UI.
Building the public chat home page
To begin, create a components
directory in the src
directory. Then, in the components
directory, create a PublicMessagesPage.js
file and add the following code to it:
// 1
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
import Messagebox from './Messagebox';
// 2
export default function PublicMessagesPage() {
// 3
const [user, setUser] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
async function handleSendMessage(e) {
// TODO
}
// 4
return (
<div>
<div>
<div>
<h1>Public Space</h1>
<p>Post your random thoughts for the world to see</p>
</div>
<div>
{messages.map((message) => (
<Messagebox key={message.id} message={message} />
))}
</div>
<div>
<form onSubmit={(e) => handleSendMessage(e)}>
<input
type="text"
placeholder="Set your username"
value={user}
onChange={(e) => setUser(e.target.value)}
required
/>
<div>
<input
type="text"
placeholder="Type your message..."
value={message}
onChange={(e) => setMessage(e.target.value)}
required
/>
<button onClick={(e) => handleSendMessage(e)}>Send</button>
</div>
</form>
</div>
</div>
</div>
);
}
This is what’s happening in the above code:
1. You import the required NPM packages.
2. You define the PublicMessagesPage
functional component.
3. You define the state
variables for PublicMessagesPage
component using useState
React hook:
- user
stores the current user’s name
- message
stores the currently typed message
- messages
stores all the messages sent and received in the present session
4. You return the JSX template for the Public Messages Page.
> Note: The styling part of the app was skipped because understanding the functionality is more important for now and styling the app can be done according to your preference.
Next, you need to implement the Messagebox
component.
Creating the message box component
To start, in the components
directory, create a Messagebox.js
file and add the following code to it:
// 1
export default function Messagebox({ message }) {
const formatDate = (value) => {
if (!value) return '';
return new Date(value).toLocalTimeString();
};
// 2
return (
<div>
<div>
<p>
<b>{message.user}</b>
</p>
<p>{message.message}</p>
<p>{formatDate(message.createdAt)}</p>
</div>
</div>
);
}
Here are the above code’s specifics:
1. You define a stateless functional component Messagebox
and pass the message
object as its parameter.
2. You return the HTML template for the Messagebox
component.
At this point, save your progress and restart the React.js development server by running the following command in your terminal:
npm start
Visit localhost:3000, and based on the way you’ve styled your app, you’ll get something like this:

Sending messages
With the frontend UI established, the next step is to enable message sending. In the src/components/PublicMessagesPage.js
file, you need to implement the handleSendMessage
method. So start by updating the handleSendMessage
function by adding the following code:
async handleSendMessage(e) {
// 1
e.preventDefault();
// 2
if (!user) {
alert('Please add your username');
return;
}
// 3
if (!message) {
alert('Please add a message');
return;
}
try {
// 4
await Axios.post('/new-message', {
user: user,
message: message,
});
} catch (error) {
console.error(error);
}
}
Here are the specifics for the above code:
1. You use the `e.preventDefault()` method to prevent the page from reloading as the form is submitted.
2. You validate whether the user
state variable is empty. If empty, you alert the user to add their username.
3. You also validate whether the message
state variable is empty. If empty, you alert the user to add a message.
4. You make a POST request (Axios.post
) to the /new-message
API endpoint after the validation is successful and send the user
and message
state variables as a payload.
Once a POST request is made to the Laravel endpoint, the MessageEvent
event is fired. So next, you need to listen to this event in your React.js app.
Listening to events
To listen to the events, add the useEffect
React hook to the PublicMessagesPage
component in the src/components/PublicMessagesPage.js
file:
// 1
useEffect(() => {
// 2
Axios.defaults.baseURL = process.env.REACT_APP_API_BASE_URL;
// 3
const echo = new Echo({
broadcaster: 'pusher',
key: process.env.REACT_APP_MIX_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443,
disableStats: true,
encrypted: true,
});
// 4
echo
.channel('public.room')
.subscribed(() => {
console.log('You are subscribed');
})
// 5
.listen('.message.new', (data) => {
// 6
setMessages((oldMessages) => [...oldMessages, data]);
setMessage('');
});
}, []);
In the above code, here are the specifics:
1. You define the useEffect
React hook that is called immediately after the `PublicMessagesPage` component is mounted.
2. You define the base URL (Axios.defaults.baseURL
) for the Axios package on which it makes the HTTP requests to the backend API.
3. You create a new Echo
instance and subscribe to the public.room
channel on the backend.
4. You subscribe to the public channel (public.room
) on which the events are being broadcast.
5. You listen to the message.new
event and pass the callback function that receives the data
sent as the event response. Since you have defined a custom broadcast name using the broadcastAs
method, you need to add the .
character before the event name. This addition instructs the Echo
not to prepend the application’s namespace to the event name.
5. You update the messages
state variable by adding the new message payload (data
) to the existing messages (oldMessages
) in the state.
Finally, save your progress and reload the React.js development server. You’ll now be able to send messages to the public chat room:

Testing the app
To test your chat app, open the localhost:3000 in at least two browser tabs or windows and try sending messages by setting different usernames. Here’s how the app will behave:

Conclusion
That’s it! In this tutorial, you learned how to create a real-time chat app using React.js, Laravel, and Ably. You also tested if your app was functioning correctly. Depending on your requirements, you can discover how to add more features to your chat app in the awesome Ably documentation. It’s exciting to hear about how you’ll use Ably in your applications.
The entire source code for this tutorial is available in this GitHub repository.