We learn as we go, we write as we learn.

About michelada.io

By joining forces with your team, we can help with your Ruby on Rails and Javascript work. Or, if it works better for you, we can even become your team! From e-Commerce to Fintech, for years we have helped turning our clients’ great ideas into successful business.

Go to our website for more info.


Reactive views from server and 0 JavaScript?

25th March 2020


In 2011, a new programming language appeared with a simple focus in mind: Making a programming language concurrent, elegant, easy, blasting fast, and powered by one of the monsters ever built, Erlang.

Elixir is a functional language built on top of Erlang, providing a delightful syntax pretty similar to Ruby, it is designed to scale tons of thousands of threads concurrently and enabling communication between them via messages. Elixir and erlang also promote a really nice expression things will go wrong and this is because it is designed to fail, offering a lot of mechanisms such as supervisors restarting particular parts or threads of the system instead of a complete explosion.


Phoenix is to Elixir what Rails is to Ruby: an amazingly fast web framework for building web applications without compromising scalability or maintainability.

By taking advantage of Phoenix speed, channels, and HTTP 2.0 implementation, Phoenix's builders came out with a cool library for Phoenix called LiveView which provides a really cool way to make reactive views with 0 Javascript and just Elixir.

Let's stop talking and let's write some code!

Having in mind that you already installed NodeJS in your computer, we first want to have Elixir installed

brew install elixir

Mix is a build tool that ships with Elixir that provides tasks for creating, compiling, testing your application, managing its dependencies and much more.

Now that we have it installed, let's install Phoenix

mix archive.install hex phx_new 1.4.0

Now we are all set to create our new Phoenix app

mix phx.new reactivity

A lot of things are going to start happening so, when mix asks us to Fetch and install dependencies?, just type y.

Now, let's set up our database and start our server

cd reactivity
mix ecto.create
mix phx.server

And know in our browser, we can go to localhost:4000


Now we are ready to set up LiveView

First, let's open our mix.exs file and add LiveView as a dependency and then run mix deps.get

def deps do
    {:phoenix_live_view, "~> 0.10.0"},
    {:floki, ">= 0.0.0", only: :test}

After getting all the dependencies, let's restart our server by pressing ctr+c twice and then mix phx.server

Now, let's open config/confix.exs and add out endpoint configuration. You can generate your own salt by running mix phx.gen.secret 32

config :reactivity, ReactivityWeb.Endpoint,
   live_view: [
     signing_salt: "YOUR_SALT_GOES_HERE"

Now we need to configure our browser pipeline:

# lib/my_app_web/router.ex
import Phoenix.LiveView.Router

pipeline :browser do
  plug :fetch_session
- plug :fetch_flash
+ plug :fetch_live_flash

Then, add the following imports to our web file in lib/reactivity_web.ex:

# lib/reactivity_web.ex

def controller do
  quote do
    import Phoenix.LiveView.Controller

def view do
  quote do
    import Phoenix.LiveView.Helpers

def router do
  quote do
    import Phoenix.LiveView.Router

Then, we will configure our endpoint:

# lib/reactivity_web/endpoint.ex

defmodule ReactivityWeb.Endpoint do
  use Phoenix.Endpoint

  # ...

  socket "/live", Phoenix.LiveView.Socket,
    websocket: [connect_info: [session: @session_options]]

  # ...

Now, let's add LiveView JavaScript dependency:

  "dependencies": {
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html",
    "phoenix_live_view": "file:../deps/phoenix_live_view"

And let's install it:

npm install --prefix assets

Now, let's enable the socket communication:

// assets/js/app.js
import {Socket} from "phoenix"
import LiveSocket from "phoenix_live_view"

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});

And finally, and optionally, you can add the default CSS:

/* assets/css/app.css */
@import "../../deps/phoenix_live_view/assets/css/live_view.css";

Perfect! We can now restart our server and check if everything is OK!



Now that we have everything set up, we can start by writing our first Controller, which will have the specific action for our route and will render our LiveView!

Let's go ahead and create a new file under /lib/reactivity_web/controllers/ called to_do_controller.ex and add the following:

defmodule ReactivityWeb.ToDoController do
  use ReactivityWeb, :controller

  def index(conn, _params) do
    live_render(conn, ReactivityWeb.ToDoView)

Then, add the route to lib/reactivity_web/router.ex

  scope "/", ReactivityWeb do
    pipe_through :browser

    get "/", PageController, :index
    get "/todo", ToDoController, :index


Add a new folder index /lib/reactivity_web/ called live and a file inside it called to_do_view.ex with the next content:

defmodule ReactivityWeb.ToDoView do
  use Phoenix.LiveView

  # Method that returns the HTML code that will be rendered
  def render(assigns) do
        <span> <%= format_date(@time) %></span>

  # This is the first function that gets called once the live view is loaded
  def mount(_session, _, socket) do
    # Check if the socket is correctly connect we send a tick in the server and falls in handle_info(:tick, process)
    if connected?(socket), do: Process.send_after(self(), :tick, 1000)

    # Here we assign all the variables needed for our view
    {:ok, assign(socket, time: :calendar.local_time())}
  # This function receives the tick event and the socket that triggered the event
  def handle_info(:tick, socket) do
    # So we send another tick after 1 second
    Process.send_after(self(), :tick, 1000)
    # And reply to our view with the local time, which ends up in rendering the view again
    {:noreply, assign(socket, time: :calendar.local_time())}    

  # Helper to format the date into a readable Hour
  def format_date(date) do
    {_, {h, m, s}} = date


And now, if we go to localhost:4000/todo, we'll see

As you can see, the view is being rendered without making any kind of event from the browser: the server knows that every second a :tick event is being triggered and the handle_info(:tick, socket) function handles this event and updates the state or assigns making the view render again.

Now, let's add some more code in order to build a simple To-Do list.

First, we'll add a new variable called todos:

  def mount(_session, _, socket) do
    if connected?(socket), do: Process.send_after(self(), :tick, 1000)
    {:ok, assign(socket, time: :calendar.local_time(), todos: [])}

Add a form and a loop for rendering the tasks in our To-Do list. Here's the form will an option called phx_submit and a value of :save: this means that, whenever the form is submitted, LiveView will trigger an event called :save with the data in the form.

  def render(assigns) do
        <span> <%= format_date(@time) %></span>

        <%= f = form_for :todo, "#", [phx_submit: :save] %>
          <%= label f, :task %>
          <%= text_input f, :task %>

          <%= submit "Save" %>

          <%= for task <- @todos do %>
            <li><%= task %></li>
          <% end %>

Now to handle that event, let's add a new handle_event function:

  #This function will handle the save event on submit form 
  def handle_event("save", %{"todo" => %{"task" => task}}, socket) do
    # And we will add the new task to out tasks array
    {:noreply, update(socket, :todos, &([task |&1]))}

And now in the browser, you will see:


And that's it!

No JavaScript needed and a whole world of opportunities around Elixir, Phoenix and this amazing library LiveView. There's a ton of things you can build with this and you don't need a lot of languages and stuff trying to interact with each other. Elixir has been growing along with its community, so I think it's the best time to start learning it and build amazing stuff with it.

And here is the full code

View Comments