Now that you have your app up and running we can start adding functionality. The first thing out chat app needs to be able to do is communicate in Phoneix. This is done via a Channel which uses websockets. Websockets create connections similar to pipes or land line telephones being that they are always open or available. The other forms of communication that are typically used on the web are more like sending snail mail-- a message is sent, the message is received and then a reply is sent. An example of this would be HTTP.

Setting up Our Channels

Phoenix includes a built-in mix task for channels that will create files and do some of the boilerplate work for you. We will need to pass a name into our command, so we will follow the Slack theme, and call it random. Look out for any actions that running the command asks you to complete.

$ mix random

In web/channels/user_socket.ex add the following below the `## Channels` comment.

## Channels
# channel "room:*", SlackirWeb.RoomChannel
channel "random:lobby", SlackirWeb.RandomChannel # Add this line

In our web/channels/random_channel.ex you should have the following content

defmodule SlackirWeb.RandomChannel do
  use SlackirWeb, :channel

  def join("random:lobby", payload, socket) do
    if authorized?(payload) do
      {:ok, socket}
      {:error, %{reason: "unauthorized"}}

  # Channels can be used in a request/response fashion
  # by sending replies to requests from the client
  def handle_in("ping", payload, socket) do
    {:reply, {:ok, payload}, socket}

  # It is also common to receive messages from the client and
  # broadcast to everyone in the current topic (random:lobby).
  def handle_in("shout", payload, socket) do
    broadcast socket, "shout", payload
    {:noreply, socket}

  # Add authorization logic here as required.
  defp authorized?(_payload) do

Now that we have generated some code let's look at what it does. First the random_channel.ex is the channel equivalent of a controller in Phoenix, being that it handles websocket requests instead of http requests. On line 2 :channel is referring to the Phoenix channel function from user_socket.ex file. It takes a parameter which is used as a namespace for the websocket connections. In this instance the name space is "random:lobby".

The next argument Slackir.RandomChannel is made up of two parts. The first part Slackir is the name of the app. The second part RandomChannel is the name of the module that handles the elixirgirls channel. In Phoenix, it’s conventional to namespace all your modules with your app name first as this prevents namespace errors.

The file, user_socket.ex, is like the router.ex file, but for channels. The topic, for us it's elixirgirls, allows clients, also known as web browsers, to subscribe to the channel. In the same way a path in a web request directs the request to the right controller, the topic directs the socket to the right module, in this case, Slackir.RandomChannel.

Now, let’s look at the join function at line 4. def join ("random:lobby", payload, socket) do: takes three arguments. The first argument, the name of the channel, "random:lobby", makes it so that this function is only called when the client is joining the specific channel. The second argument, payload is the request from the user; it can contain auth credentials, a message or anything the user wants. Finally, it takes socket, which is the websocket connection. There’s a test, authorized?, which always returns true. Then, {:ok, socket} just returns a status and the websocket they are connecting to. join always returns {:ok, socket} to allow all connections to the channel.

Once a user joins a channel they can send messages to the channel and they’ll be received by one of the handle_in messages. The first one just returns the payload sent by the user back to them. The second one sends the payload to all the users subscribed to the channel. It’s the second that allows us to build our chat room with just a little front end work. When you send a message to the chat room, this function sends it back to you and to everyone else in the channel.

To handle the connections on the Client-side we’ll start by adding jQuery to our lib/slackir_web/templates/layout/app.html.eex:

<script src="//"></script>

Phoenix comes with a simple javascript socket client, but it’s disabled by default. Go into your assets/js/app.js and uncomment the last line:

import socket from "./socket"

Then go into your assets/js/socket.js and paste the following:

let channel ="random:lobby", {});
let list    = $('#message-list');
let message = $('#message');
let name    = $('#name');

message.on('keypress', event => {
  if (event.keyCode == 13) {
    channel.push('shout', { name: name.val(), message: message.val() });

channel.on('shout', payload => {
  list.append(`<b>${ || 'Anonymous'}:</b> ${payload.message}<br>`);
  list.prop({scrollTop: list.prop("scrollHeight")});

  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) })"random:lobby", {}) sends the join request to the server, and the server sends the message back to the client. Here, we listen for a keypress event on the message text field. Whenever the user enters a message, it’s pushed on the channel and the text field is cleared. When there’s an incoming message on the channel, it’s appended to the div we previously created and scrolled to the bottom.

Next we will want to create the container for our messages: Open lib/slackir_web/templates/page/index.html.eex, and replace its contents with:

<div id='message-list' class='row'>

<div class='row form-group'>
  <div class='col-md-3'>
    <input type='text' id='name' class='form-control' placeholder='Name' />
  <div class='col-md-9'>
    <input type='text' id='message' class='form-control' placeholder='Message' />

What we have done here is create an empty div that will list all the chat messages and added two text fields, one for the user’s name and one for the message. Now you will want to open assets/css/app.css and paste this at the end:

#message-list {
  border: 1px solid #777;
  height: 400px;
  padding: 10px;
  overflow: scroll;
  margin-bottom: 50px;

Now go to and see the results!