Phoenix LiveView is Elixir's real-time web framework that lets you build interactive apps without writing JavaScript. The entire stack is free and open source.
Why Phoenix in 2026?
Phoenix handles 2 million concurrent WebSocket connections on a single server. While Node.js struggles at 10K connections and Rails needs Redis for pub/sub, Phoenix does it natively with Erlang's BEAM VM.
What You Get for Free
- LiveView — real-time UI without JavaScript
- PubSub — built-in real-time messaging (no Redis needed)
- Presence — track who's online (built-in, no external service)
- Channels — WebSocket support out of the box
- Ecto — powerful database query DSL
- Mix — build tool, test runner, formatter, all built in
LiveView: Interactive UI Without JS
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
def render(assigns) do
~H"""
<div class="text-center p-8">
<h1 class="text-4xl font-bold"><%= @count %></h1>
<div class="space-x-4 mt-4">
<button phx-click="decrement" class="btn">-</button>
<button phx-click="increment" class="btn">+</button>
</div>
</div>
"""
end
def handle_event("increment", _, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
def handle_event("decrement", _, socket) do
{:noreply, update(socket, :count, &(&1 - 1))}
end
end
This is a fully interactive counter — click events, state updates, DOM patches — all over WebSocket. Zero JavaScript written.
Real-Time Chat in 30 Lines
defmodule MyAppWeb.ChatLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(MyApp.PubSub, "chat:lobby")
end
{:ok, assign(socket, messages: [], message: "")}
end
def handle_event("send", %{"message" => msg}, socket) do
Phoenix.PubSub.broadcast(MyApp.PubSub, "chat:lobby", {:new_message, msg})
{:noreply, assign(socket, message: "")}
end
def handle_info({:new_message, msg}, socket) do
{:noreply, update(socket, :messages, &[msg | &1])}
end
def render(assigns) do
~H"""
<div class="chat-container">
<div :for={msg <- Enum.reverse(@messages)} class="p-2 border-b">
<%= msg %>
</div>
<form phx-submit="send" class="mt-4">
<input name="message" value={@message} placeholder="Type..." class="input" />
<button type="submit" class="btn">Send</button>
</form>
</div>
"""
end
end
Real-time chat. Multiple users. No Redis. No Socket.io. No JavaScript.
Presence Tracking (Who's Online)
defmodule MyAppWeb.Presence do
use Phoenix.Presence,
otp_app: :my_app,
pubsub_server: MyApp.PubSub
end
# In your LiveView:
def mount(_params, _session, socket) do
if connected?(socket) do
MyAppWeb.Presence.track(self(), "room:lobby", socket.assigns.current_user.id, %{
name: socket.assigns.current_user.name,
joined_at: DateTime.utc_now()
})
end
presences = MyAppWeb.Presence.list("room:lobby")
{:ok, assign(socket, users_online: map_size(presences))}
end
Quick Start
# Install Elixir
brew install elixir # macOS
# or: curl -fsSL https://elixir-lang.org/install.sh | bash
# Create Phoenix project
mix archive.install hex phx_new
mix phx.new my_app --live
# Setup and run
cd my_app
mix setup
mix phx.server
Phoenix vs Next.js vs Rails
| Feature | Phoenix | Next.js | Rails |
|---|---|---|---|
| Real-time | Built-in (LiveView) | Needs Socket.io | Needs ActionCable + Redis |
| Concurrency | 2M connections/server | ~10K connections | ~1K connections |
| Presence | Built-in | External service | External service |
| PubSub | Built-in | Redis/external | Redis |
| Fault tolerance | Erlang supervisors | Process crashes | Process crashes |
| Hot reload | Built-in | Built-in | Built-in |
Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.
Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.
Top comments (0)