Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
phoenix liveview logo over an abstract tech background
Web Development

Building an entire web app with Phoenix LiveView

Dawid Boksa

At the start of this year, I had the pleasure and opportunity of building an entire application using Phoenix LiveView. I did so without writing any JavaScript code (if we can exclude initialising date pickers on some inputs!).

Please note that in this post, I’ll be describing my personal experiences and chosen approaches while using Phoenix LiveView. This isn't a step-by-step tutorial on how to create a web app in LiveView, and each person who uses the program for similar tasks will undoubtedly take an individual approach to how they work.

This was an exciting task for me, as it was my first contact with LiveView. As I familiarised myself with the new paradigm, I found the app building process to be refreshing, as it presented me with a completely different approach to building certain features.

Not only was this an exciting and pleasant task — building an app in Phoenix LiveView was also surprisingly fast. This is largely because LiveView made it easy to build new features, and coding doesn't require switching the context between building the backend in Elixir and frontend in JavaScript. Using LiveView, I could simply focus on building the web app as a whole without any differentiation between frontend and backend.

How I approached the project

Developers typically think about the flow of communication between client and server taking the form of a mail-like conversation. That is, each request comes with a response containing the most information that we are allowed to send and that could be important for a specific page. This means that, in practical terms, we’re preloading a lot of data. (This is otherwise known as a stateless web approach.)

The ‘search’ function is bound to every input inside the form ‘change’ event

In LiveView, the communication exchange is more like a dynamic conversation. The client and server are conversing through the WebSocket, dynamically changing the state of a page. Because of this, I didn't need to think of GET requests, what to preload, and what should be fetched from the server. Instead, I would simply create a function inside the ‘#{page}Live (i.e. ‘Projects.IndexLive’) module and bind the function to the specific element in the browser. In other words, I would be changing the value in a search input placed in a projects index to filter the projects card, keeping only ones that match the search. Thus, the entire connection between my browser and the server is stateful.

Live modules in place of controllers

Generally, I like controllers, especially when they’re well maintained and slim. But I came to like LiveView’s modules a lot more. So how did I efficiently manage them in this case? Inside the "live" folder (for LiveView modules), I created a folder for each resource that I need, just as I would create the controllers for these resources.

Then, inside these folders, I created files for every template that I wanted to use. For example:

  • ‘index_live.ex’
  • ‘show_live.ex’
  • ‘new_live.ex’

A basic live module for the project index with search functionality

Okay, but what about non-GET actions? I simply treated them as events and handled them inside these modules too. So, the ‘create’ action is handled in ‘new_live.ex’ and the ‘update’ action will be handled in ‘edit_live.ex’. This allowed me to nicely encapsulate and compartmentalise the functionality.

What if my module grows and goes from pretty, clean, and slim to “big and messy” code? I’ve encountered this issue, since my web app had some pretty conditional parts that weren't the right fit for the context modules. Fortunately, creating the helper module and calling functions from it helped address this issue.

LiveView routers

The insides of a router

Normally, Phoenix routers allow you to match HTTP requests to controllers' actions. It’s quite a similar case in LiveView, but instead of the controller action, it's matched to the live module and is handled with the ‘mount/3 function.

In the router, instead of keywords like "get" and "post", you use "live" and point to the LiveView module. In my opinion, the result looks quite clean, especially when you’re scoping this in a way that maintains proper structure based on contexts.

Integration with custom JavaScript

As I’ve mentioned above, I didn't need to write any custom JavaScript for my web app (aside from initialising date pickers); all of the interactions were made with LiveView. That being said, there are some ideas that I have after this experience on how to improve on it — for example, I would probably try to use Vue or Alpine for certain things, like showing modals, and other things needing immediate frontend interactions or changes in DOM.

Client hooks

I'm currently researching this topic and it looks quite achievable, especially building this around hooks and events. To me, it’s worth it to use custom JavaScript. However, don’t reinvent the wheel if something is easily achievable with plug and play libraries, like Alpine.js.  

In general, I think it’s always best to draw a proper line between what should be done with JavaScript and what should be done with the LiveView. Whichever you choose, sticking with your decision throughout the app building process should help to ensure and maintain consistency.

Takeaways from the app building process

Through the process of building my app, I’ve gathered a few highlights from my experience using LiveView that I think could be useful for other developers seeking to try it out for themselves:

  1. Phoenix LiveView provides a way to build a dynamic and modern website without writing specific JavaScript code just for the sake of dynamicity.
  2. As I’ve demonstrated through this post, it's quite easy to start and become productive with LiveView if you’re already familiar with Phoenix.
  3. The benefits of building LiveView components over the separation between API and SPA is significant. Also, the joy and simplicity in which the same developer can address everything in a single language is a massive boon.
  4. Finally, it's a nice and refreshing task to try to handle common web app challenges with LiveView.
Development
Phoenix

Subtitle

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis,

List item

List item

List item

List item

Design is not just what it looks like and how it feels. Design is how it works

- Steve Jobs

Subtitle

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis,

List item

List item

List item

List item

Lorem Ipsum voluptate velit
Lorem Ipsum voluptate velit

Subtitle

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis,

List item

List item

List item

List item