Web apps used to be solitary places. One user, one session, one screen. But modern applications thrive on connection — not just between user and server, but between users themselves. It’s not enough to reflect real-time state. You need to coordinate it. You need workflows where one user acts and others respond — not in minutes, but in milliseconds.
Phoenix LiveView makes this possible with an architecture that bakes real-time into every interaction. Underneath it all is a single, persistent WebSocket per user — a conduit that carries messages, events, presence signals, and coordination hints between the people who use your app. It’s fast. It’s scalable. But more importantly, it’s expressive.
Let’s unpack how to build these kinds of workflows — not just reactive UIs, but orchestrated systems — using LiveView’s core tools and a few strategic patterns.
Step One: Define the Conversation, Not the Page
It starts with a mental shift. In a traditional app, you define screens. In LiveView, you define conversations — message flows, state transitions, and updates that span multiple views.
Imagine an approval workflow. A user submits a request. A manager reviews it. An auditor observes. Each participant has their own LiveView, their own session, their own context — but they’re all watching the same process unfold.
Your job isn’t to build three UIs. Your job is to build one shared timeline, visible from different angles.
This is where Phoenix PubSub becomes your routing layer for event flow. You define a topic like:
`elixir
`
"request:#{request_id}"
Then every LiveView involved in that request — requester, reviewer, observer — subscribes:
elixir
Phoenix.PubSub.subscribe(MyApp.PubSub, "request:#{request_id}")
Now, when one user acts, everyone else knows. There’s no polling. No delay. Just a single message:
`
elixir
Phoenix.PubSub.broadcast(MyApp.PubSub, "request:#{request_id}", {:status_changed, "approved"})
`
Each LiveView updates independently. The requester sees a status badge flip. The reviewer’s action buttons disappear. The observer sees a timestamp tick. Three views. One moment.
Step Two: Track Who’s Watching
To make workflows truly dynamic, you need to know who’s online and what they’re doing. This is where Phoenix.Presence
shines. It lets each LiveView broadcast metadata about the current user’s role, location, and state.
Say an auditor is watching a transaction log. You want to display “1 auditor viewing” at the bottom of the screen. You also want to send a discreet alert to that user when a new action is taken.
Each connected LiveView pushes presence metadata like this:
`elixir
`
track(socket, "request:#{id}", user.id, %{role: "auditor", viewing: true})
Now you can list viewers by role, broadcast targeted messages (only to auditors), or even trigger UI changes based on who’s present. This isn’t just convenience. It’s architecture. When you model people, not just data, your workflows become social.
Step Three: Delegate Control Across Roles
In many workflows, control shifts from one role to another. The requester starts the flow. The reviewer takes action. The observer watches it land. These handoffs must feel instant — but also intentional.
LiveView makes it easy to model role-based UI states. But it’s the WebSocket layer that makes handoffs seamless.
When a reviewer approves a request, they can trigger:
elixir
Phoenix.PubSub.broadcast(MyApp.PubSub, "request:#{id}", {:approved_by, reviewer_id})
The requester’s LiveView listens and responds:
- Replace “Pending…” with “Approved by Alex”
- Show a follow-up form
- Enable a download button
At the same time, the reviewer’s view transitions into a summary. The observer sees a final state. And your app has just performed a distributed state transition — across users, roles, and devices — in real time.
Step Four: Resolve Conflicts and Collisions
Multi-user workflows introduce complexity. What happens when two people act at once? Who wins? How do you prevent inconsistent state?
The answer isn’t to lock the UI. It’s to design for event ordering and optimistic updates.
When a LiveView receives an incoming WebSocket message, it can choose to:
- Accept it and re-render
- Ignore it based on local state
- Retry or rollback if conflicts arise
This logic belongs in your handle_info/2
callbacks. You don’t need to write locking code. You just need to treat every update as provisional — something the server validates and the view adapts to.
If two reviewers click “approve” at once, only the first gets through. The second gets a status update saying, “Already approved by Alex.” No error. No race condition. Just clear communication.
Step Five: Notify at the Speed of Expectation
Workflows aren’t just about status. They’re about attention. Who needs to act? Who needs to know?
You can use targeted WebSocket broadcasts to drive notifications across roles. Not pop-ups or modals — just quiet signals that something’s changed.
- A badge appears.
- A tab pulses.
- A sidebar refreshes.
You push these with simple calls:
`
elixir
broadcast(MyApp.PubSub, "user:#{id}", {:needs_attention, :request, request_id})
`
No external push service. No client-side JS orchestration. Just pure Elixir, powered by your already-open socket.
The result? Notifications that aren’t just fast, but relevant. Not spammy, but intentional. This is what workflow orchestration feels like when it’s done right.
Wrapping Up
The magic of LiveView isn’t just real-time updates. It’s real-time coordination. You’re not just building features. You’re building timelines. You’re creating systems where users move together — not in sequence, but in sync.
Every WebSocket message becomes a moment. Every broadcast becomes a shared shift in state. And your app — instead of just showing users what happened — shows them what’s happening now.
If you're serious about using Phoenix LiveView like a pro and applying it to real-world projects, I've put together a detailed PDF guide: Phoenix LiveView: The Pro's Guide to Scalable Interfaces and UI Patterns. It's a 20-page deep dive into LiveView's advanced features, architectural best practices, and reusable patterns for building production-grade apps. Whether you're creating a new interface or refactoring an old one, this guide will save you time and help you make the most of LiveView's capabilities.
Top comments (1)
This all sounds amazing—until three people try to approve my PTO at the same time and suddenly I’m on a tropical beach… or fired. Can the PDF guide help me sync my manager’s vacation standards too?