2026-01-10
Creating interactive web experiences can quickly get complicated. Often, youβre juggling a front-end in one language and a back-end in another, all while figuring out how they communicate. LiveViews offer a simpler alternative: you can build rich, interactive pages entirely from the back-end. LiveView keeps the server and browser efficiently in sync, letting you focus on the user experience rather than worrying about updating the interface in real time. In this article, I will show how this works with a small example LiveView inspired by the John Carpenter movie They Live. In the movie, the protagonist uses a pair of sunglasses to see which humans are actually aliens. In our LiveView, you can toggle sunglasses on or off to reveal the hidden aliens.
LiveView is a solution to the problem of how to organize code to create rich, fast, interactive user experiences in the browser. In the past, the view in the browser (i.e., an HTML page) was rendered entirely on the server. This approach is straightforward to organize, since all the code runs in one place and can often be written in a single language. The downside is that pages are static once rendered. Every time the user interacts with the page, the system has to send the interaction to the server, have the server process it, and then generate a whole new page to send back to the browser. This makes even small interactions slow and cumbersome.
Later, developers moved toward the modern model of giving the front-end a much larger role, often using a JavaScript framework to build single-page applications. This allows for dynamic, real-time interactions, but introduces complexity, including multiple languages, duplicated state, and the need to manage communication between client and server. LiveView offers a middle path. It enables developers to build rich, interactive experiences while keeping most of the code on the server. The server handles updates and LiveView automatically pushes them to the browser. While LiveView relies on a persistent backend connection, for many apps this is a small price to pay since most real-world applications already need a backend for databases, authentication, or business logic. As we will see later in this article, LiveView ensures that this communication is done very efficiently. The result is pages that are interactive, fast, and much simpler to maintain.
The best way to learn how things work is with a hands-on example, so let's create our first LiveView. Although there are many implementations inspired by it, the original is part of the Phoenix web application framework written in the Elixir programming language. Assuming Phoenix is installed (see the install instructions for details) we can create and run a new Phoenix project with LiveView by running a few commands from the terminal.
To get started, we will create a new Phoenix project from the command line. Nowadays, LiveView ships with Phoenix by default, no extra install is needed. Since our example does not need a database, we can also skip setting up the database library Ecto (using the -no-ecto flag).
mix phx.new they_live --no-ecto
This will create a new Phoenix project in the they_live directory. Next we can navigate to created directory and start the Phoenix application.
cd they_live
mix phx.server
After being asked to fetch the dependencies, we should now have a Phoenix app up and running. With a browser we can navigate to its main endpoint (e.g. http://localhost:4000/).
Next, we are going to add an endpoint under /sunglasses with our LiveView.
Create a new file at lib/they_live_web/live/sunglasses_live.ex where the module of our LiveView will be:
defmodule TheyLiveWeb.SunglassesLive do
use TheyLiveWeb, :live_view
def mount(_params, _session, socket) do
people = [
%{id: 1, type: :human},
%{id: 2, type: :alien},
%{id: 3, type: :human},
%{id: 4, type: :human},
%{id: 5, type: :alien},
%{id: 6, type: :human}
]
{:ok, assign(socket, sunglasses: false, people: people)}
end
def handle_event("toggle_sunglasses", _value, socket) do
{:noreply, update(socket, :sunglasses, &(!&1))}
end
def render(assigns) do
~H"""
<h1>They Live View π</h1>
<button phx-click="toggle_sunglasses">
<%= if @sunglasses do %>
( π Sunglasses On )
<% else %>
( π Sunglasses Off )
<% end %>
</button>
<p>
<%= if @sunglasses do %>
You can see the aliens now!
<% else %>
Everything looks normal.
<% end %>
</p>
<div style="margin-top: 1rem; font-size: 2rem;">
<span
:for={person <- @people}
:key={person.id}
style="margin-right: 0.5rem;"
>
<%= if person.type == :alien and @sunglasses do %>
π½
<% else %>
π
<% end %>
</span>
</div>
"""
end
end
Next, add a route in the file lib/they_live_web/router.ex. This can be done by entering the following route:
live "/sunglasses", SunglassesLive
under the main route, so we will have in the file
get "/", PageController, :home
live "/sunglasses", SunglassesLive
Now visiting http://localhost:4000/sunglasses will show us our LiveView. We should see the following initially:
They Live View π
( π Sunglasses Off )
Everything looks normal.
π π π π π π
And after pressing the button (i.e. clicking on anything between the brackets) we put on the sunglasses and reveal the aliens:
They Live View π
( π Sunglasses On )
You can see the aliens now!
π π½ π π π½ π
Now that we have our working LiveView, letβs go over all the code in this module to better understand what a LiveView does.
The top-level portion defines a module TheyLiveWeb.SunglassesLive and specifies it to be a LiveView:
defmodule TheyLiveWeb.SunglassesLive do
use TheyLiveWeb, :live_view
...
end
This line brings in all the LiveView functionality. It marks this module as a special kind of page that can maintain state on the server and update the browser in real time.
Inside the module there are three main parts: a mount function, a handle_event function, and a render function. These are the core pieces of a LiveView. At a high level, a LiveView is a server-side process that starts from an initial state, receives and handles events from the browser, updates its state, and then renders any updates.
The key responsibilities of these functions are:
Letβs go over each one in more detail:
mount
This function runs when the page first loads. It sets up the initial state of the LiveView. In our example, we define people (a list of humans and aliens) and sunglasses (whether the sunglasses are on or off). This state lives on the server and is the βsource of truthβ for the page. Everything that is rendered to the front end is derived from these values.
handle_event
This function responds to user actions. When the user clicks the sunglasses button, this function is called. It updates the state by flipping sunglasses on or off. After the state changes, LiveView automatically re-renders the parts of the page that need updating. You do not have to write any JavaScript or manually manipulate the page.
render
This function defines what the page should look like based on the current state. In our example:
Whenever the state changes, LiveView calls the render function again, compares it to the previous render, and sends only what actually needs to change to the browser. This keeps interactions fast and smooth and avoids sending a full HTML page back each time.
You can see this in action in the browserβs console or network logs. LiveView maintains a persistent WebSocket connection between the browser and server. When you toggle the sunglasses, the server does not resend the entire page. For example, the page heading They Live View π is never sent again. Only the button label, the explanatory text, and the emoji row are updated. In the latest LiveView version (1.1 onwards), even the human emojis in the row remain untouched, so only the aliens are updated. Here is a literal fragment of the update payload sent over the wire:
{
"p": {
"0": ["\n ( π Sunglasses On )\n "],
"1": ["\n You can see the aliens now!\n "],
"2": ["\n π½\n "]
}
}
LiveView is even smart enough to figure out that you only need one patch for the alien emoji, which is applied in multiple positions in the emoji row.
Of course, this is just a small example. LiveView with Phoenix can do much more: nested components, forms, real-time notifications, presence tracking, and hooks (escape hatches for when you really need JavaScript). Phoenix comes with Tailwind and DaisyUI integration out of the gate so you can get started with nicely styled components. You can build rich, interactive applications without getting bogged down managing a separate front-end framework and its communication with the server.
With LiveView, you can get things done, keep your pages interactive, and still have time to chew bubblegum (as long as you are not out of gum).