Screencast
31. Phoenix Presence with LiveView
When building collaborative web applications, knowing who else is viewing the same content creates better user awareness and prevents conflicts. Phoenix Presence provides a robust, distributed solution for tracking user presence across your application. This system automatically handles the complexity of distributed tracking - if you’re running multiple servers, it keeps everything synchronized without additional configuration.
The foundation starts with a simple Presence module that connects to your PubSub system. Once configured, you can track users on any topic, whether it’s customer records, project dashboards, or shared documents. The video demonstrates how to build a complete, reusable system that shows user avatars when multiple people view the same resource.
defmodule TutorialWeb.Presence do
use Phoenix.Presence,
otp_app: :tutorial,
pubsub_server: Tutorial.PubSub
def track_user(topic, user) when is_binary(topic) do
user_id = to_string(user.id)
user_initials = user.email |> String.upcase() |> String.slice(0..1)
user_meta = %{
id: user_id,
email: user.email,
initials: user_initials,
joined_at: System.system_time(:second)
}
track(self(), topic, user_id, user_meta)
end
end
Create Reusable Live Components for Presence
Building presence functionality as a Live Component makes it portable across your entire application. The component handles its own subscription logic, user tracking, and display rendering. When mounted, it automatically subscribes to presence events for the specified topic and begins tracking the current user.
The component design focuses on simplicity - it requires only a current user scope and a unique topic string. Topics like “project:123” or “customer:456” ensure presence tracking stays isolated to specific resources. The video shows how this modular approach lets you add presence awareness to any LiveView page with just a few lines of code.
def update(%{current_scope: current_scope, topic: topic} = assigns, socket) do
if connected?(socket) do
Presence.subscribe(topic)
if user = current_scope && current_scope.user do
Presence.track_user(topic, user)
end
end
online_users = Presence.list_users(topic)
{:ok, socket |> assign(assigns) |> assign(:online_users, online_users)}
end
Handle Real-Time Presence Updates
Phoenix Presence automatically broadcasts join and leave events, but your LiveView needs to handle these messages and update the component accordingly. The pattern involves catching presence diff messages at the LiveView level and forwarding them to the component using send_update/2
.
This approach keeps the presence logic contained within the component while allowing the parent LiveView to route the real-time updates. When users navigate between pages or close their browsers, the presence list updates immediately for all other viewers. The video explains how this creates seamless collaboration awareness without complex polling or manual refresh logic.
@impl true
def handle_info({TutorialWeb.Presence, diff_info}, socket) do
send_update(TutorialWeb.OnlineUsersLiveComponent,
id: "online-users",
presence_diff: diff_info
)
{:noreply, socket}
end
What You Will Learn
- How to set up Phoenix Presence module and add it to your supervision tree
- Creating track_user, untrack_user, and list_users functions for presence management
- Building a reusable Live Component that handles presence subscriptions and display
- Implementing real-time presence updates using handle_info callbacks
- Structuring presence topics to isolate tracking per page or resource
- Best practices for presence UI design and user avatar display