Modern Talking with Elixir: Messenger App Tutorial with Phoenix LiveView
  • Elixir

Modern Talking with Elixir: Messenger App Tutorial with Phoenix LiveView

Have you ever wondered why Elixir and its ecosystem is gradually becoming the go-to platform for many web application developers who want both performance and productivity, not a tradeoff between them?

Well, we'll show you why - and we'll do it via a thorough crash course of Phoenix Framework's hot deal, Phoenix LiveView, using which we'll build a Messenger-like live chat application.

As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!

In a series of articles (don't forget to subscribe to our newsletter!), we'll convince you that Phoenix LiveView will revolutionize the way you create reactive UIs!

Why Phoenix?

The thing about Elixir, on top of Erlang/OTP, is that it offers a great mix of making life easy and being a scalable and reliable platform that will not let you down when your estimated traffic of 4000 users becomes 4,000,000 users.

Phoenix Framework is Elixir's answer to the never-ending question of how to build rich web applications, and it's got a lot of tools that make the job easy - one of the latest being Phoenix LiveView.

Long story short, LiveView is a tool that lets an Elixir developer create reactive UIs without writing a single line of JS code. Which is great, given that many Elixir developers do not exactly consider themselves fluent at JS, or - just like myself - are not exactly in love with JS.

Lessons learnt from reactive UI libraries

Many JavaScript frameworks, both contemporary and not-so-contemporary ones, rely on manipulating the page's DOM for dynamic content updates.

Historically, for instance, developers using BackboneJS would define a Backbone.View to represent an atomic chunk of user interface, behind which there's a Backbone.Model, encapsulating the business logic of data.

Backbone remained unopinionated about how views were to be rendered, so it had no built-in tools to make the re-rendering of views on model changes efficient - the whole structure of a view had to be built from scratch and replaced, which tended to yield inefficient views.

In contrast, modern frameworks such as ReactJS or Vue.js don't care about how the data model layer works at all (loosely coupled data stores such as Redux are often used for this) - but they have a virtual DOM concept - long story short, a pattern of incrementally upgrading only those elements that need to be changed, based on changes in the state of particular components and their children.

The challenge, though, is pretty much down to how to exchange data between the UI and the backend. You will usually need to implement a JSON API or a GraphQL service, or perhaps you could develop a WebSocket-based solution using Phoenix Channels.

Either way, the Pareto 80/20 principle will imminently catch you, and when you get to the 20% of work needed to finish off your message-passing code, it'll soon become a framework within a framework.

Why, where & how LiveView excels

Phoenix LiveView's concept is both groundbreaking and familiar, in different ways.

It is familiar in that it lets you define UI elements as nestable components composed of pure HTML markup, and it builds upon the experience of reactive UI frameworks in implementing mechanisms that calculate diffs between consecutive UI states to ensure efficient updates.

It is groundbreaking in the way it maintains the states of components and manages their updates - in Phoenix LiveView, components are stateful on the server, and their events and updates are communicated via a bidirectional WebSocket connection.

Phoenix LiveView is built on top of Elixir processes and Phoenix Channels - every LiveView instance is a BEAM process, acting very much like a GenServer, receiving messages and updating its state.

While modern JS frameworks such as React have server-side rendering capabilities, it is usually not convenient to do this in a non-NodeJS backend server. Rendering content via JavaScript often results in SEO issues, and some trickery is needed for search engines to index the page correctly. In Phoenix LiveView, the initial render is static as in the classic HTML request-response cycle, so you'll get good Lighthouse scores and it won't hurt your SEO.

Erlang easily maintains thousands of processes concurrently, and Phoenix authors have even managed to make it handle 2 million WebSocket connections on a single (albeit pretty strong) machine. With the server using Elixir's strengths to manage LiveView states, the client-side logic can be thin and simple.

In fact, as stated in the introduction, in most LiveView-powered apps you won't write a single line of JS code. In many cases, when interacting with an element whose update is supposed to fetch data for a new UI state from the server, the workflow using a reactive JS framework would be:

  1. Handle the element's change event
  2. Send a request to the server containing the actual changes
  3. Receive response and update state store based on response data
  4. Let the view layer re-render the changed DOM elements

This involves annotating HTML elements so that they can be identified by JS code, writing browser-side scripts to handle the element's state change event, send a payload to the server, which processes the request as part of a Phoenix controller action.

With Phoenix LiveView, you only write HTML and Elixir code, with the JS part being handled by a script bundled with the LiveView package.

Phoenix LiveView basic usage

The basic idea behind Phoenix LiveView is very simple and straightforward. Be sure to subscribe to our newsletter to learn more!

LiveView is an Elixir behaviour, and your most basic LiveView definition will consist of two callback implementations:

  • A render/1 function, containing the template of how your component is represented in HTML, with elements of the component's state interpolated. This is much like defining an ordinary view. The special ~L sigil is used to interpolate assigns into your EEx syntax, and convert it into an HTML-safe structure.
  • A mount/2 function, wiring up socket assigns and establishing the LiveView's initial state.
  defmodule YourappWeb.CounterLive do
    use Phoenix.LiveView

    def render(assigns) do
      ~L"""
      <a href='#' phx-click='increment'>
        I was clicked <%= @counter %> times!
      </a>
      """
    end

    def mount(params, socket) do
      {:ok, assign(socket, :counter, 0)}
    end
  end

However, the whole fun of using LiveView is managing its state, and the next two callbacks will come in handy.

  • A handle_event/3 function, handling events coming from the browser. Noticed the phx-click attribute in our template's link? This is the name of an event that will be transported to the LiveView process via WebSockets. We'll define a function clause that will match to the event's name.
  def handle_event("increment", params, %{assigns: %{counter: counter}} = socket) do
    {:noreply, assign(socket, :counter, counter + 1)}
  end

It will mutate the LiveView's state to have a new, incremented value of the counter, and the render/1 function will be called with the new assigns.

The second argument, here named params, is of special interest as well, because - in the case of a phx-click event - it contains the event's metadata:

  %{
    "altKey" => false,
    "ctrlKey" => false,
    "metaKey" => false,
    "pageX" => 399,
    "pageY" => 197,
    "screenX" => 399,
    "screenY" => 558,
    "shiftKey" => false,
    "x" => 399,
    "y" => 197
  }

We trust that you won't now hesitate to try it out with a <form> tag and a phx-change attribute to see what event metadata are passed when a form element's value is changed. Either way, we'll explore this in more detail in later episodes of this tutorial - stay tuned and subscribe to our newsletter so that you don't miss out!

  • A handle_info/2 callback, handling events coming from anywhere but the browser. This means events sent from external sources (remember a LiveView is just an Elixir process, so you can do whatever's needed in order for it to receive messages!), or events sent from the LiveView to itself. For instance, it takes this to increment the counter every 5 seconds:
  def mount(params, socket) do
    if connected?(socket), do: :timer.send_interval(5000, self(), :increment)

    {:ok, assign(socket, :counter, 0)}
  end

  def handle_info(:increment, %{assigns: %{counter: counter}} = socket) do
    {:noreply, socket |> assign(:counter, counter + 1)}
  end

To reduce code repetition, you could make handle_event/3 send a message to self() that triggers the same handle_info/2 routine.

You can now access your LiveView as a standalone route - to do this, put this in your router.ex:

import Phoenix.LiveView.Router

scope "/", YourappWeb do
 live "/counter", CounterLive
end

...or render the LiveView within any other template:

<%= Phoenix.LiveView.live_render(@conn, YourappWeb.CounterLive) %>

The Curious Messenger Roadmap

We'll make you familiar with how to wield the Phoenix LiveView sword, and you'll build a fully-fledged Messenger replacement, which will make you (almost) forget about any other instant messaging app you had ever used before...

Phoenix LiveView is obviously only part of the story, and there's a lot more ground that we'll cover. We'll do a few episodes, each of which touches a different set of concerns that we'll have to consider when designing the app.

Keep #BusyBeingCurious - hopefully you weren't too bored with our introductory talk. The real thing is coming very soon - stay tuned and subscribe!

A girl receiving new message

Sign to our Newsletter

We're committed to your privacy. Curiosum uses the information you provide to us to contact you about our relevant content. You may unsubscribe from these communications at any time. For more information, check out our privacy policy.

Michał Buszkiewicz, Elixir Developer
Michał Buszkiewicz Curiosum Founder & CTO

Read more
on #curiosum blog

Phoenix LiveView Tutorial: Adding Phoenix PubSub and Pow Authentication to Messenger
  • Elixir

Phoenix LiveView Tutorial: Adding Phoenix PubSub and Pow Authentication to Messenger

We've already bootstrapped our Phoenix LiveView-based Messenger app's database structure and a first LiveView page.

This time, we're going to improve real-time communication between the app's users using Phoenix PubSub, and use the Pow library to add secure user authentication.

As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!

5 top-tier companies that use Elixir
  • Elixir

5 top-tier companies that use Elixir

Elixir is a pretty capable language - and it consistently ranks near the top of most loved and wanted languages rankings. It has a large following and some very persuasive preachers as well. But that would not be enough to make me like it – what I need as real proof of its strengths is real businesses that strive with Elixir.

That’s what this list is all about – a bunch of stories from top companies that chose Elixir and never looked back. Let us show you how its power and versatility shows in practice.