What's the difference between Phoenix and LiveView?

Could someone please explain the difference between phoenix and liveview? Is liveview necessary for ANY dynamic content?

Without liveview, could i build a counter with increment and decrement functions that updates its state dynamically on the front end?

Im just beginning to learn phoenix, and was just curious how far phoenix could take me without liveview; for no real reason. Thanks in advance!

1 Like

You can build a counter on a web page with simple javascript that never contacts the server, so you don’t need LiveView. Save the following html/javascript in a file and load it in your browser:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Counter</title>
</head>

<body>
    <div id="counter">0</div>
    <button id="incr">Increment</button>
    <button id="decr">Decrement</button>

    <script>
        "use strict"
    
        function incr(event) {
            let div = document.querySelector("#counter") 
            let current_val = Number(div.innerHTML)
            div.innerHTML = current_val + 1
        }
        function decr(event) {
            let div = document.querySelector("#counter") 
            let current_val = Number(div.innerHTML)
            div.innerHTML =  current_val - 1
        }
    
        document.querySelector("#incr").addEventListener("click", incr)
        document.querySelector("#decr").addEventListener("click", decr)
       
    </script>
</body>
</html>

And, with a little more work, you can write javascript that contacts the server, then updates the web page with data returned by the server.

What LiveView does is it lets you execute a lot of very complex javascript, where the web page is instantaneously updating the server with its state, and the server is instantaneously updating the contents of the web page-without knowing or writing any javascript.

Liveview is a part of Phoenix (obviously) and cannot work without Phoenix framework. Phoenix can be used for serving Liveviews, dead views and APIs.

Dead views cannot interact with Phoenix except to send another request for another page or submit a form unless using Phoenix channels.

Phoenix channels opened up the possibility of sending events to clients over websocket and those pages may be served by controllers serving dead views. This requires client side javascript for interactivity (which is served as part of your dead view page). Outside of Phoenix channels Phoenix will not have any interaction with the client until the client sends another HTTP request (traditional web).

Liveview uses a similar approach to Phoenix channels by using a web socket from the client to the Phoenix server to communicate over. In the case of LiveView the client interracts with a LiveView Elixir process that maintains state on the server for that client. The client can then interact with the server process over that socket, to send events, forms etc and receive an updated view in a highly interactive manner. Liveview optimizes sending the refreshed view back to the client by only sending the data that changed as the html is mostly static and doesn’t need to be sent more than once on the initial page render. This can make Liveview more efficient than most APIs for updating views.

This does mean interactivity requires a round trip and therefore latency can be an issue for certain UI interactions. Using a Phoenix JS helpers can help with this as can using Liveview hooks. However for me the real sweet spot is custom elements (web components) so you can almost write static views and get very nice interactivity encapsulated in custom elements (for example Shoelace) without having to go down a JavaScript framework path.

2 Likes

Here is an example of defining and using a custom element <my-counter> in regular html:

<!DOCTYPE html>
<html lang="en">
  <my-counter></my-counter>

  <script type="module">
    import { LitElement, html } from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';

    class Counter extends LitElement {
      static get properties() {
        return {
          count: { type: Number },
        };
      }

      constructor() {
        super();
        this.count = 0;
      }

      setCount = count => {
        this.count = count;
      };

      render() {
        const { count } = this;
        return html`
          <h1>Lit Element - Counter</h1>
          <h2>You clicked ${count} times</h2>
          <button type="button" @click=${() => this.setCount(count - 1)}>Decrement</button>
          <button type="button" @click=${() => this.setCount(count + 1)}>Increment</button>
        `;
      }
    }

    customElements.define('my-counter', Counter);
  </script>
</html>

In my project I just put web components like above (the part within the script tag) into my assets folder, import them from assets/app.js and then use them in my app like regular html elements. It’s a nice way to get enriched UI be it dead views or live views without buying into a JS framework.

You can see the above in action here.

2 Likes