What Is Phoenix LiveView?
Phoenix LiveView is a library for the Phoenix Framework that lets you build rich, real-time, interactive user interfaces in server-rendered Elixir—without writing a separate JavaScript front-end, REST/GraphQL API, or client-side state layer. The server holds the UI state, and LiveView pushes minimal DOM updates to the browser over a persistent WebSocket. The result feels like a single-page app but is built with the simplicity of server-rendered HTML.
In one sentence: LiveView renders HTML on the server, opens a WebSocket, and sends only the diffs when state changes—so you get real-time, interactive UIs with one language, one codebase, and the server as the single source of truth.
How LiveView Works: The Lifecycle
Understanding the LiveView lifecycle is the key to the whole model:
- Initial render: The first request renders a normal, fully server-rendered HTML page (great for SEO and fast first paint).
- Connect: The browser then opens a persistent WebSocket and the same LiveView re-mounts as a stateful process on the server.
- Events: User interactions (clicks, form input, key presses) are sent over the socket as events to your
handle_event/3callbacks. - Diff and patch: When your server state changes, LiveView re-renders, computes the minimal diff, and pushes only the changed bytes to the browser, which patches the DOM.
A minimal LiveView module shows how little code this takes:
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
def handle_event("inc", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
def render(assigns) do
~H"""
<button phx-click="inc">Count: {@count}</button>
"""
end
endNo API endpoint, no client-side framework, no manual WebSocket handling—the framework wires it all together. The mechanics are documented in depth in the official Phoenix LiveView docs on HexDocs.
Getting Started: Your First LiveView
A fresh Phoenix app already ships with LiveView configured. Generate one and you are ready to add a live route in minutes:
# Install the latest Phoenix project generator
mix archive.install hex phx_new
# Generate a new app (LiveView is included by default)
mix phx.new my_app
cd my_app
mix ecto.createAdd a live route in lib/my_app_web/router.ex and create the matching module:
# router.ex
scope "/", MyAppWeb do
pipe_through :browser
live "/counter", CounterLive
endStart the server with mix phx.server, open /counter, and you have a real-time page—no build step, no separate front-end project, no API client. The Phoenix Framework handles asset bundling and the socket connection for you.
Core LiveView Features, With Code
Forms and Live Validation
Forms are where LiveView shines. Wire phx-change for keystroke-level validation and phx-submit for the final save—both run against your Ecto changeset on the server, so client and server can never disagree:
def handle_event("validate", %{"user" => params}, socket) do
changeset =
%User{}
|> User.changeset(params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, form: to_form(changeset))}
end
def handle_event("save", %{"user" => params}, socket) do
case Accounts.create_user(params) do
{:ok, user} -> {:noreply, put_flash(socket, :info, "Saved!")}
{:error, changeset} -> {:noreply, assign(socket, form: to_form(changeset))}
end
end
# In the template:
# <.form for={@form} phx-change="validate" phx-submit="save"> ... </.form>Real-Time Updates With PubSub
To push updates to every connected user, broadcast over Phoenix PubSub and handle the message in handle_info/2. This is how live dashboards, chat, and activity feeds stay in sync with no polling:
def mount(_params, _session, socket) do
if connected?(socket), do: Phoenix.PubSub.subscribe(MyApp.PubSub, "orders")
{:ok, assign(socket, orders: Orders.list())}
end
# Anywhere in your app:
# Phoenix.PubSub.broadcast(MyApp.PubSub, "orders", {:new_order, order})
def handle_info({:new_order, order}, socket) do
{:noreply, update(socket, :orders, &[order | &1])}
endStreams for Large Collections
LiveView streams let you render and update large lists without keeping the whole collection in server memory—the client holds the DOM, the server sends only inserts, updates, and deletes:
def mount(_params, _session, socket) do
{:ok, stream(socket, :messages, Chat.list_messages())}
end
def handle_info({:new_message, msg}, socket) do
{:noreply, stream_insert(socket, :messages, msg)}
end
# Template:
# <div id="messages" phx-update="stream">
# <div :for={{id, msg} <- @streams.messages} id={id}>{msg.body}</div>
# </div>What You Can Build with LiveView
- Live dashboards: Real-time metrics and charts that update without polling.
- Forms with live validation: Validation that feels instant because it runs over the socket, not a full HTTP round trip.
- Collaborative UIs: Multiple users editing the same resource and seeing each other’s changes live (paired with Phoenix Presence and PubSub).
- Notifications and activity feeds: Pushed in real time with no separate notification service.
- Multi-step wizards: Complex stateful flows where the server owns the state machine.
Phoenix LiveView vs a React SPA
A typical real-time stack pairs a React SPA with a REST/GraphQL API, WebSocket handlers, and a client state library—four moving parts to keep in sync. LiveView collapses these into one server-side layer. You trade some client-side richness and offline capability for dramatically less complexity and a single source of truth. Here is how the two stacks compare head to head:
| Dimension | Phoenix LiveView | React SPA |
|---|---|---|
| Language / stack | Elixir only, one codebase | JavaScript/TypeScript + a backend |
| API layer | None needed | REST or GraphQL required |
| State source of truth | Server | Client (plus server sync) |
| First paint / SEO | Server-rendered HTML, fast | Needs SSR/hydration setup |
| Client-side latency | Server round trip per event | Instant local interaction |
| Offline support | Limited | Strong (PWA, local state) |
| Overall complexity | Low—fewer moving parts | Higher—more to wire up |
| Best fit | CRUD + real-time products | Offline-first, highly custom UIs |
For deeply interactive, offline-first, or highly custom client UIs, a JavaScript front-end still wins; for most CRUD-plus-real-time products, LiveView ships faster with far less code. LiveView also supports JS hooks for the cases where you do need bespoke client behavior.
Testing Phoenix LiveView
LiveView ships with Phoenix.LiveViewTest, which drives your live pages in-process—no browser, no flaky end-to-end harness. You mount a view, trigger events, and assert on the rendered HTML:
defmodule MyAppWeb.CounterLiveTest do
use MyAppWeb.ConnCase
import Phoenix.LiveViewTest
test "increments the counter", %{conn: conn} do
{:ok, view, html} = live(conn, "/counter")
assert html =~ "Count: 0"
assert view
|> element("button")
|> render_click() =~ "Count: 1"
end
endBecause these tests run against the real LiveView process without a browser, they are fast enough to cover full interaction flows in your normal mix test suite.
Deploying and Scaling LiveView
LiveView keeps one lightweight stateful process per connected user, so deployment has a few characteristics worth planning for:
- Persistent connections: Every active user holds a WebSocket. Put a load balancer that supports WebSockets in front, and allow long-lived connections.
- Sticky sessions are not required for the socket itself, but a re-connect re-mounts the LiveView, so keep
mount/3cheap and idempotent. - Clustering: Use
libclusterto connect BEAM nodes and Phoenix PubSub (with the PG2 or Redis adapter) so a broadcast on one node reaches subscribers on every node. - Memory: Each connection is a few KB of process memory. A single modest server comfortably holds tens of thousands of concurrent LiveView connections thanks to the BEAM.
The concurrency and fault-tolerance of the BEAM and OTP are exactly why LiveView scales the way it does—a crashed connection is isolated and supervised, not a page-wide failure.
When Not to Use LiveView
LiveView keeps a stateful connection per user, so it shines when users are connected and interacting. It is less suited to fully offline apps, latency-sensitive UIs for users on very poor networks, or pure public/static content where no interactivity is needed (plain controllers are simpler there). As always, match the tool to the workload.
Frequently Asked Questions
What is Phoenix LiveView used for?
LiveView is used to build real-time, interactive web UIs—dashboards, live-validated forms, collaborative tools, notifications—in server-rendered Elixir, without a separate JavaScript front-end or API layer. The server holds state and pushes minimal DOM diffs over a WebSocket.
Phoenix LiveView vs React: which should I use?
Choose LiveView for CRUD-plus-real-time products where you want one Elixir codebase, no API layer, and the server as the single source of truth—you ship faster with far less code. Choose React when you need offline-first behavior, very low client-side interaction latency on already-loaded pages, or a highly custom client UI. Many teams use LiveView for most of the app and drop in JS hooks or a React island only where bespoke client behavior is required.
Is LiveView faster than React?
For time-to-first-render and development speed, LiveView is often faster because it sends server-rendered HTML and removes the API + client-state layers. For client-side interaction latency on already-loaded pages, a React SPA can feel snappier since it avoids a server round trip. The right choice depends on how interactive and offline-capable the UI must be.
Do I need JavaScript to use LiveView?
No JavaScript is required for the core interactive model—events and DOM updates are handled by the framework. For bespoke client behavior (custom animations, third-party widgets), LiveView provides JS hooks, but most apps need little to no custom JavaScript.
How do I test Phoenix LiveView?
Use Phoenix.LiveViewTest, which drives live pages in-process without a browser. Mount the view with live/2, trigger interactions with helpers like render_click/1 and render_submit/1, and assert on the returned HTML. These tests run in your normal mix test suite and are fast enough to cover full interaction flows.
How does Phoenix LiveView scale?
Each connected user is one lightweight BEAM process holding a WebSocket, using only a few KB of memory, so a single modest server handles tens of thousands of concurrent connections. To scale horizontally, connect nodes with libcluster and use Phoenix PubSub so broadcasts reach subscribers across every node. Put a WebSocket-aware load balancer in front and keep mount/3 cheap so reconnects are fast.
Is Phoenix LiveView production-ready?
Yes. LiveView is mature, widely used in production, and backed by the BEAM’s fault tolerance and concurrency. It scales to large numbers of concurrent connections per server and is a first-class part of the Phoenix Framework.
Where LiveView Fits in the Phoenix Ecosystem
LiveView is the front-end half of Phoenix’s real-time story. For how it compares to other stacks, see Phoenix vs Node.js and Ruby on Rails vs the Phoenix Framework, or read why we choose it for products in why Elixir Phoenix is the best framework for SaaS. For our full Elixir practice, see our Phoenix framework development page.
Let Equantra Build It for You
We have seen what happens when teams choose the right framework from day one versus having to rewrite later. At Equantra, Elixir Phoenix is one of our core specialties alongside Ruby on Rails, Django, and Next.js.
Whether you are building a new real-time product with LiveView, migrating an existing application to Elixir for better performance, or need a dedicated development team to scale your engineering capacity, we can help.
Our team brings:
- Production experience building and maintaining Elixir Phoenix applications
- End-to-end delivery from architecture design through deployment and ongoing support
- A dedicated team model where you get committed engineers, not a rotating pool of contractors
- US-focused service with engineers who understand your market and your timezone
Get a free consultation to discuss your project, or explore our custom software development services to see how we work, or hire Elixir developers directly.