How to build a ‘who’s typing’ feature with Laravel and Pusher 
Tags: laravel, vue, pusher
Published: 2018-01-15 12:04:22

---
title: How to build a ‘who’s typing’ feature with Laravel and Pusher 
published: true
description: 
tags: laravel, vue.js, pusher
---

When you're using a chat app, having a 'who's typing' feature really improves the user experience and makes it easier for users to interact with the application. If you're chatting with someone, you can easily see when the other person is responding to your chats, saving you time from typing **'you there?'** and let you know when to wait for the other person's response, so you don't keep sending messages.

In this tutorial, I will show you how to build a 'who's typing' feature with Laravel and Pusher, using the concept of a chat app to demonstrate it. Note that this tutorial focuses on the 'who's typing' part of the app (if you want to learn how to build a chat app using Laravel and Pusher, you should read <a href="https://blog.pusher.com/how-to-build-a-laravel-chat-app-with-pusher/">this tutorial</a>)

Let's take a quick look at what we'll be building:

<img src="http://blog.pusher.com/wp-content/uploads/2017/03/whos-typing-laravel-pusher.gif" alt="" width="800" height="450" class="alignnone size-full wp-image-2894" />

The code of the completed demo is available on [GitHub](https://github.com/ammezie/laravel-whos-typing).

## Setting Up Laravel

Create a new Laravel project. (I prefer using the Laravel installer) Open your terminal and run the code below:

```language-bash
laravel new laravel-whos-typing
```
Next, we need to setup our new Laravel project. First, we need to register the `App\Providers\BroadcastServiceProvider`. Open `config/app.php` and uncomment `App\Providers\BroadcastServiceProvider` in the `providers` array.

We then need to tell Laravel that we are using the Pusher driver in the `.env` file:

``` language-txt
// .env

BROADCAST_DRIVER=pusher
```
Since we specified we want to use Pusher as our broadcasting driver, we need to install the Pusher PHP SDK:

``` language-bash
composer require pusher/pusher-php-server
```
## Setting Up Pusher

If you don't have one already, create a free Pusher account at https://pusher.com/signup then log in to your dashboard and create an app. Take note of your app credentials as we'll be using them shortly.

Now, let's fill in our Pusher app credentials. If you open the `config/broadcasting.php`, you'll notice that Laravel is pulling some of the Pusher credentials from the `.env` file. So let's update the `.env` file to contain our Pusher app credentials:

``` language-txt
// .env

PUSHER_APP_ID=xxxxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
```
Remember to replace the `x`s with your Pusher app credentials. You can find your app credentials under the **Keys** section on the **Overview** tab in the Pusher Dashboard.

Also, remember to fill in the `cluster` of your Pusher app and other additional options:

``` language-php
// config/broadcasting.php

'options' => [
   'cluster' => 'eu',
   'encrypted' => true
],
```
## Installing Frontend Dependencies

For this tutorial, we'll be using `Bootstrap`, `Vue` and `Axios`, which have been setup for us by Laravel, though we still need to install each of the dependencies. To compile our CSS and JavaScript, we need to install Laravel Mix, which is a wrapper around Webpack. We can install these dependencies through `NPM`:

``` language-bash
npm install
```
We also need to install Laravel Echo, which is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel and of course the Pusher JavaScript library:

``` language-bash
npm install --save laravel-echo pusher-js
```
Once installed, we need to tell Laravel Echo to use Pusher. At the bottom of the `resources/assets/js/bootstrap.js` file, uncomment the Laravel Echo section and update the details with:

``` language-js
// resources/assets/js/bootstrap.js

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: Laravel.pusherKey,
    cluster: 'eu',
    encrypted: true
}); 
```
Since we specified our Pusher app credentials in the `.env` file, notice `Laravel.pusherKey` from the code above, we'll load our Pusher app key from the config instead of hard coding it directly in `resources/assets/js/bootstrap.js`. We'll define `Laravel.pusherKey` in a later section.

Also use the same `cluster` that you specified earlier in `config/broadcasting.php`.

With the setups done, let's start implementing a 'who's typing' feature in our Laravel application.

To do this, we need to know when a user is typing into the chat box. There are different ways to accomplish this, but in this tutorial, we'll add event listeners to the chat box. With this, we can know when a user is actually typing a message and display the typing indicator to the appropriate users.

We'll make use of JavaScript keyboard events: `onkeydown` which fires event when a user is pressing a key. This means that when a user presses a key, we send events to Pusher. These types of events are called `client events` and do not hit the server at all. But how do we broadcast such events to Pusher since they are a bit different from the normal events (server to Pusher events)? Well, Pusher is aware of such events and has a special way of handling them.

By default, when you create a Pusher app, client events are not enabled. We have to enable this for our app. To enable client events in your Pusher app, select the app then click on the **App Settings** tab and then check the box next to **Enable client events**.

<img src="http://blog.pusher.com/wp-content/uploads/2017/03/whos-typing-laravel-pusher-enable-client-event.png" alt="" width="1124" height="461" class="alignnone size-full wp-image-2895" />

Once we have enabled client events in our Pusher app, we can now trigger and listen for client events in our chat app.

## Authenticating Users

Our chat app will require users to be logged in before they can begin to chat. So, we need an authentication system:

``` language-bash
php artisan make:auth
```
This will create the necessary routes, views and controllers needed for an authentication system.

Before we go on to create users, we need to run the `users` migration that comes with a fresh installation of Laravel. But to do this, we first need to setup our database. Open the `.env` file and enter your database details:

``` language-txt
// .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-chat
DB_USERNAME=root
DB_PASSWORD=root
```
Update with your own database details. Now, we can run our migration:

``` language-bash
php artisan migrate
```
Note: There's a bug in Laravel 5.4 if you're running a version of MySQL older than 5.7.7 or MariaDB older than 10.2.2. More info [here](https://github.com/laravel/framework/issues/17508). This can be fixed by replacing the `boot()` of `app/Providers/AppServiceProvider.php` with:

``` language-php
// app/Providers/AppServiceProvider.php

// remember to use
Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
  Schema::defaultStringLength(191);
}
```
## Defining App Routes

Open `routes/web.php` and replace the routes with the code below:

``` language-php
// routes/web.php

Auth::routes();

Route::get('/', function () {
    return view('chat');
})->middleware('auth');
```
The routes are pretty simple: a route that will handle authentication and a route to the homepage that will render a chat view which we'll create shortly.

**NOTE**: Since we have removed the `/home` route, you might want to update the `redirectTo` property of both `app/Http/Controllers/Auth/LoginController.php` and `app/Http/Controllers/Auth/RegisterController.php` to:

``` language-php
protected $redirectTo = '/';
```
## Creating The Chat App View

Create a new `resources/views/chat.blade.php` file and paste into it:

``` language-html
// resources/views/chat.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Chats</div>

                <div class="panel-body">
                    <ul>
                        <li v-for="message in messages">
                            @{{ message.user.name }} - @{{ message.message }}
                        </li>
                    </ul>
                    <div>
                        <div class="input-group">
                            <input type="text" name="message" class="form-control" placeholder="Type your message here..." v-model="newMessage" @keyup.enter="sendMessage">
                            <span class="input-group-btn">
                                <button class="btn btn-primary" @click="sendMessage">
                                    Send
                                </button>
                            </span>
                        </div>
                        <span v-show="typing" class="help-block" style="font-style: italic;">
                            @{{ user }} is typing...
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```
Once again pretty straightforward, we are using Vue here. We loop through each of the messages and display them. Next, there is a input field and a send button for composing chat messages. The input field is binded to the `newMessage` data. When the send button is clicked or the enter key is pressed on the input field, a `sendMessage()` is called. Lastly, there is span holding the 'is typing' indicator. This will be hidden by default and will be displayed using Vue's `v-show` when `typing` is `true` (that is when a user is typing).

Notice that we are displaying the name of the user along with the 'is typing' indicator, we need a way to pass the authenticated user to our JavaScript file. Remember from `resources/assets/js/bootstrap.js`, where we used `Laravel.pusherKey`, we also need to pass Pusher app key to our JavaScript file. We can do this by updating the `` section in `resources/views/layouts/app.blade.php` (which was created when we ran `make:auth`) with:

``` language-js
resources/views/layouts/app.blade.php

<!-- Scripts -->
<script>
  window.Laravel = {!! json_encode([
    'csrfToken' => csrf_token(),
    'user' => Auth::user(),
    'pusherKey' => config('broadcasting.connections.pusher.key'),
  ]) !!};
</script>
```
The code above creates a `Laravel` object on the global window, we then add some items to the object. The `user` item will be the currently authenticated user and the `pusherKey` item will load our Pusher app key from the `config/broadcasting.php` config file.

Laravel has integrated Vue and Axios for us, so we can start using `Vue` without any further setup. There is an `app.js`file that Laravel creates by default within `resources/assets/js/app.js`. Open this file and update with the code below:

``` language-js
// resources/assets/js/app.js

require('./bootstrap');

const app = new Vue({
    el: '#app',

    data: {
        messages: [],
        newMessage: '',
        user: '',
        typing: false
    },

    methods: {
        sendMessage() {
            // add new message to messages array
            this.messages.push({
                user: Laravel.user,
                message: this.newMessage
            });

            // clear input field
            this.newMessage = '';

            // persist to database
        }
    }
});
```
First, we require the `resources/assets/js/bootsrap.js` file that contains our package setups and integrations. Next, we create a new Vue instance and bind it to **app** `id`. We then create some data variables: `messages` array will hold our chat messages, `newMessage` will be the new message that a user sends, `user` will be the currently authenticated user and finally `typing` will hold `true` or `false` indicating whether a user is typing or not.

Next we define a `sendMessage()` that simply adds the new message along with the user that sent it to the messages array and finally clears the input field. (If you are building an actual chat app, you might want to do an AJAX request to persist the new message to the database here.)

Having done this, we can start sending messages and our messages will be displayed on the chat view. Now let's move on to the meat of this tutorial; adding 'who's typing' to our Laravel application.

Laravel Echo provides some handy methods to integrate with client events which we'll be using to implement our 'who's typing' indicator.

Paste the code below into the `resources/assets/js/app.js` within the `methods` object:

``` language-js
// resources/assets/js/app.js

isTyping() {
  let channel = Echo.private('chat');

  setTimeout(function() {
    channel.whisper('typing', {
      user: Laravel.user,
        typing: true
    });
  }, 300);
},
```
The `isTyping()` will be triggered when an **onkeydown** event is fired within the chat input field (that is, when a user is typing a message). First, we subscribe to a private channel called `chat` and trigger a client event using Laravel Echo's `whisper()` after 0.3s. The `whisper()` accepts the name of the client event, in our case `typing` and the data we want to broadcast. Since Pusher specifies that client events must be prefixed by `client-`, Laravel is smart enough to prefix the `client-` for us under the hood. We pass the user that is typing a message and typing as `true` as the data we are broadcasting.

**Note:** Client events can only be triggered on private and presence channels because they require authentication. Also, client events are not delivered to the originator of the event. For more information on client events, kindly checkout the Pusher [documentation](https://pusher.com/docs/client_api_guide/client_events#trigger-events).

Since we created a private channel, only authenticated users will be able to listen on the `chat` channel. We need a way to authorize that the currently authenticated user can actually listen on the channel. This can be done by in the `routes/channels.php` file:

``` language-php
// routes/channels.php

Broadcast::channel('chat', function ($user) {
  return Auth::check();
});
```
We pass to the `channel(),` the name of our channel and a callback function that will either return `true` or `false` depending on whether the current user is authenticated.

Now that we can trigger client events, we also need a way to listen for the client events in our application. To do this, add the code below to `resources/assets/js/app.js` just after the `data` object:

``` language-js
// resources/assets/js/app.js

created() {
  let _this = this;

  Echo.private('chat')
    .listenForWhisper('typing', (e) => {
      this.user = e.user;
      this.typing = e.typing;

      // remove is typing indicator after 0.9s
      setTimeout(function() {
        _this.typing = false
      }, 900);
    });
},
```
Again we subscribe to the `chat` channel. To listen for client events, we use the `listenForWhisper()` and set both the `user` and `typing` data accordingly. Lastly, we remove the is typing indicator after 0.9s of a user not typing.

Before we start testing the who's typing feature, let update the input field of the `chat` view with:

``` language-html
// resources/view/chat.blade.php

<input type="text" name="message" class="form-control" placeholder="Type your message here..." v-model="newMessage" @keydown="isTyping" @keyup="notTyping" @keyup.enter="sendMessage">
```
We added two keyboard events: `@keydown` and `@keyup` which are Vue equivalent of the JavaScript keyboard events we talked about earlier. On keydown, the `isTyping()` will be triggered and on keyup, the `notyping()` will be triggered.

We can now compile the JavaScript files using Laravel Mix using:

``` language-bash
npm run dev
```
Now we can start our chat app by running:

``` language-bash
php artisan serve
```
## Conclusion

With such a basic app, we have seen how to implement a 'who's typing' feature in a Laravel application using Pusher. I hope you find this tutorial helpful and if you encounter any problems following this tutorial, kindly drop a comment below and we'd tackle them together.

This post was originally posted by the author on the [Pusher blog](https://blog.pusher.com/build-whos-typing-feature-laravel-pusher).