Definition
LiveView is a Phoenix framework feature that enables rich, real-time user interfaces rendered entirely on the server and delivered over WebSocket connections. Unlike traditional Single Page Applications (SPAs) that run JavaScript frameworks like React or Vue in the browser, LiveView maintains all UI state on the server as an Elixir process, sending minimal DOM diffs to the client for efficient updates. This architecture eliminates the need for separate frontend and backend codebases while providing a responsive, interactive experience that rivals client-side frameworks.
The core insight behind LiveView is that modern network latencies (typically 10-50ms for most users) are fast enough that server-rendered updates feel instantaneous for the vast majority of UI interactions. By keeping state on the server, LiveView eliminates entire categories of frontend engineering problems: state synchronization between client and server, API versioning, client-side caching invalidation, and the security implications of running business logic in the browser. The trade-off is a persistent WebSocket connection per user session and slightly higher server memory usage compared to stateless HTTP request handling.
LiveView was created by Chris McCord (also the creator of Phoenix) and first released in 2019. It has since evolved into a comprehensive framework for building interactive web applications, with features including live navigation (SPA-like page transitions without full page reloads), file uploads with real-time progress, and a component system that supports both stateless function components and stateful LiveComponents with their own lifecycle.
How LiveView Works
Understanding LiveView's internal mechanics is essential for building performant applications and debugging unexpected behavior.
The Connection Lifecycle
When a user navigates to a LiveView route, two separate renders occur:
-
Static HTML render (HTTP): The initial page load renders the LiveView as static HTML on the server and sends it to the browser. This ensures the page is visible immediately, is SEO-friendly, and works even before the WebSocket connects.
-
Live render (WebSocket): Once the page loads, the browser's LiveView JavaScript client establishes a WebSocket connection. The server spawns a dedicated GenServer process for this connection and performs a second mount, this time with the
connected?check returning true. From this point forward, all interactions happen over the WebSocket.
defmodule PrismaticWeb.PerimeterDashboardLive do
use PrismaticWeb, :live_view
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Prismatic.PubSub, "perimeter:updates")
Process.send_after(self(), :refresh_metrics, 30_000)
end
{:ok,
socket
|> assign(:assets, PrismaticPerimeter.list_assets())
|> assign(:security_rating, PrismaticPerimeter.current_rating())
|> assign(:page_title, "Perimeter Dashboard")}
end
@impl true
def handle_event("scan_domain", %{"domain" => domain}, socket) do
case PrismaticPerimeter.initiate_scan(domain) do
{:ok, _scan} ->
{:noreply, put_flash(socket, :info, "Scan initiated for #{}")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Scan failed: #{}")}
end
end
@impl true
def handle_info(:refresh_metrics, socket) do
Process.send_after(self(), :refresh_metrics, 30_000)
{:noreply, assign(socket, :security_rating, PrismaticPerimeter.current_rating())}
end
@impl true
def handle_info(%{type: :asset_discovered} = event, socket) do
{:noreply, update(socket, :assets, fn assets -> [event.asset | assets] end)}
end
endDOM Diffing and Patching
When server-side state changes (via handle_event, handle_info, or handle_params), LiveView re-renders the template, computes the difference between the previous and current rendered output, and sends only the changed portions to the browser:
| Rendering Phase | What Happens |
|---|---|
| State Change | assign/2 or update/2 modifies the socket's assigns |
| Re-render | LiveView calls render/1, which evaluates the HEEx template |
| Diff Computation | LiveView compares static and dynamic parts of the template output |
| Transport | Only changed dynamic parts are sent as a compact JSON payload |
| Client Patch | The JavaScript client applies DOM patches using morphdom |
The key optimization is that HEEx templates separate static HTML (sent once) from dynamic content (tracked per-change). A template with 1,000 lines of HTML but one dynamic value will only transmit the changed value, not the surrounding markup.
Architecture
Browser <-- WebSocket --> Phoenix.Endpoint --> LiveView Process (GenServer)
|
v
render/1 (HEEx template)
|
v
DOM diff --> Browser patch
Each LiveView connection spawns a dedicated server-side process (GenServer) that:
- Manages the view's state (assigns)
- Handles user events (clicks, form submissions, keyboard input)
- Subscribes to PubSub topics for real-time updates from other processes
- Re-renders and sends DOM diffs on state changes
- Monitors connection health and cleans up on disconnect
Because each LiveView is a BEAM process, it benefits from all of BEAM's properties: fault isolation (one user's crashed view does not affect others), preemptive scheduling (no single view can block others), and per-process garbage collection (GC pauses are bounded per-view).
LiveComponents
LiveView provides two component models for organizing UI code:
Function Components (Stateless)
Function components are pure functions that receive assigns and return HEEx markup. They have no lifecycle, no state, and no process -- they are simply reusable template fragments.
defmodule PrismaticWeb.Components.SecurityBadge do
use Phoenix.Component
attr :grade, :atom, required: true
attr :score, :integer, required: true
def security_badge(assigns) do
~H"""
<span class={ items-center rounded-full px-3 py-1 text-sm font-medium #{grade_color(@grade)}
<%= @grade %> (<%= @score %>)
</span>
"""
end
defp grade_color(:A), do: "bg-green-100 text-green-800"
defp grade_color(:B), do: "bg-blue-100 text-blue-800"
defp grade_color(:C), do: "bg-yellow-100 text-yellow-800"
defp grade_color(grade) when grade in [:D, :F], do: "bg-red-100 text-red-800"
endStateful LiveComponents
Stateful LiveComponents have their own state, lifecycle callbacks (update/2, handle_event/3), and can be targeted by events independently from the parent LiveView:
defmodule PrismaticWeb.Components.AssetTableLive do
use PrismaticWeb, :live_component
@impl true
def update(assigns, socket) do
{:ok,
socket
|> assign(assigns)
|> assign(:sort_by, :risk_score)
|> assign(:sort_order, :desc)}
end
@impl true
def handle_event("sort", %{"column" => column}, socket) do
column = String.to_existing_atom(column)
order = if socket.assigns.sort_by == column, do: toggle_order(socket.assigns.sort_order), else: :asc
sorted = Enum.sort_by(socket.assigns.assets, &Map.get(&1, column), order)
{:noreply, assign(socket, sort_by: column, sort_order: order, assets: sorted)}
end
endHEEx Templates and Hooks
HEEx (HTML + EEx) is LiveView's template engine, designed specifically for server-rendered real-time UIs. It provides compile-time validation of HTML structure, component calls, and attribute types:
~H"""
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<.card :for={asset <- @assets} class="p-4">
<h3 class="text-lg font-semibold"><%= asset.domain %></h3>
<.security_badge grade={asset.grade} score={asset.score} />
<button phx-click="scan" phx-value-id={asset.id} class="mt-2 btn-primary">
Rescan
</button>
</.card>
</div>
"""JavaScript Hooks
For interactions that require client-side behavior (drag-and-drop, third-party JavaScript libraries, clipboard access), LiveView provides a Hooks API:
let Hooks = {};
Hooks.ClipboardCopy = {
mounted() {
this.el.addEventListener"click", () => {
const text = this.el.dataset.copyValue;
navigator.clipboard.writeTexttext;
this.pushEvent"copied", {: text };
};
}
};Context in Prismatic
LiveView powers all of the Prismatic Platform's real-time dashboards. The platform enforces TailwindCSS-first styling with Flowbite components for all LiveView templates, explicitly forbidding inline styles and custom CSS.
Dashboard Routes
| Route | LiveView Module | Description |
|---|---|---|
/ | DashboardLive | Main platform dashboard with system overview |
/perimeter | PerimeterLive | EASM overview with security ratings |
/perimeter/assets | PerimeterAssetsLive | Asset inventory with filtering and sorting |
/perimeter/compliance | PerimeterComplianceLive | NIS2/ZKB compliance assessment |
/perimeter/easm | PerimeterEASMLive | Advanced EASM dashboard with drill-down |
Real-Time Update Patterns
The Prismatic Platform uses three patterns for pushing updates to LiveView dashboards:
| Pattern | Mechanism | Use Case |
|---|---|---|
| PubSub broadcast | Phoenix.PubSub.broadcast/3 | Multi-user updates (new asset discovered, alert triggered) |
| Process.send_after | Scheduled self-messages | Periodic metric refresh (every 30s) |
| handle_info callback | Direct process messaging | Agent status updates, scan completion notifications |
Performance Considerations
LiveView's server-side rendering model introduces specific performance characteristics:
| Consideration | Guidance |
|---|---|
| Assign minimization | Only store data needed for rendering in socket assigns |
| Temporary assigns | Use temporary_assigns for large lists that need not persist in memory |
| Stream API | Use stream/3 for efficiently managing large collections |
| Debouncing | Use phx-debounce on form inputs to reduce server roundtrips |
| Component granularity | Use LiveComponents to scope re-renders to changed sections |
| PubSub selectivity | Subscribe only to topics the current view needs |
# Streams: efficient append/prepend without keeping full list in memory
def mount(_params, _session, socket) do
{:ok, stream(socket, :assets, PrismaticPerimeter.list_assets())}
endRelated Terms
- Phoenix - Web framework that LiveView is built into
- GenServer - Behavior underlying each LiveView process
- BEAM - Virtual machine providing process isolation for LiveView
- WebSocket - Transport protocol for LiveView connections
- PubSub - Distributed messaging for real-time LiveView updates
- Channel - Alternative real-time communication layer in Phoenix
- Flowbite - Component library used with LiveView in Prismatic
- TailwindCSS - Utility CSS framework for LiveView templates
- EASM - External attack surface module with LiveView dashboards
- Process Isolation - Crash containment per LiveView connection
- Plug - Middleware layer that routes requests to LiveView
- Endpoint - Phoenix entry point managing WebSocket upgrades
See Also
- prismatic_web -- Main LiveView application serving platform dashboards
- prismatic_perimeter_web -- Perimeter EASM LiveView dashboards
- prismatic_hawkeye_web -- HAWKEYE visitor intelligence LiveView
- prismatic_ir_pvm_web -- PVM incident response LiveView
- Architecture -- Platform architecture
- Fault Tolerance -- System reliability through process isolation
- Observability -- Monitoring LiveView performance via telemetry
Connect & Contribute
Created by Tomáš Korcak (korczis) | Open Source under GHL