At the heart of LiveView is a simple assumption:
Your app knows what it’s doing and can show users what matters — fast.
But what if your app doesn’t have the answer?
What if it needs to fetch it from an external API, legacy service, or third-party system?
Suddenly, you're at the mercy of latency, rate limits, and timeouts.
And still, your UI must stay reactive, responsive, and trustworthy.
The Core Problem
- LiveView is stateful and fast
- External APIs are stateless and slow
- The user shouldn't have to care
So how do we make real-time UIs out of non-real-time data?
Pattern 1: Async Task + send(self(), ...)
When a user triggers an event that requires external data:
# inside handle_event/3
Task.start(fn ->
response = ExternalAPI.get_some_data()
send(self(), {:api_result, response})
end)
{:noreply, assign(socket, loading: true)}
Then, handle the result:
# inside handle_info/2
def handle_info({:api_result, result}, socket) do
{:noreply, assign(socket, loading: false, result: result)}
end
🎯 Result: Socket stays alive, UI reflects state, no blocking.
Pattern 2: Webhook → PubSub → LiveView
If the API supports webhooks, integrate them with Phoenix PubSub:
- Your app receives a webhook (e.g., payment confirmation)
- It broadcasts to a user/topic:
Phoenix.PubSub.broadcast(
MyApp.PubSub,
"user:#{user.id}",
{:payment_confirmed, data}
)
- Your LiveView subscribed in
mount/3
:
Phoenix.PubSub.subscribe(MyApp.PubSub, "user:#{user.id}")
- React in
handle_info/2
:
def handle_info({:payment_confirmed, data}, socket) do
{:noreply, assign(socket, payment_status: :confirmed)}
end
✅ No polling. No JS. Just webhook-to-UI in real time.
Pattern 3: Cache + Background Refresh
For infrequently-changing external data:
- On
mount/3
, serve cached data (ETS, Redis, etc.) - Kick off async refresh:
Task.start(fn ->
new_data = ExternalAPI.fetch(...)
Cache.write(key, new_data)
send(self(), {:refreshed_data, new_data})
end)
- When the new data comes in, LiveView updates instantly.
Users get instant UX and fresh data, without delay.
Add Resilience
External APIs will fail.
Build for it:
Strategy | Benefit |
---|---|
Retries + backoff | Resilience to flakiness |
Telemetry hooks | Visibility into slow calls |
Timeout messages | Let users know something's wrong |
Fallback UI | Avoid blank screens or confusion |
Show something — loading spinners, skeleton screens, retry buttons.
Truthful UIs beat fast UIs every time.
Key Principle
Real-time doesn’t mean real-instant. It means real-state.
If your app is waiting on data, show that state clearly.
Progress bars, loading text, animated skeletons — whatever suits your UX.
Let the user feel that the system is working — even when it isn’t done yet.
Your Stack for External Integration
- ✅
Task.start
for async API calls - ✅
handle_info
for async responses - ✅
Phoenix.PubSub
for real-time updates - ✅ Webhook bridges for 3rd-party systems
- ✅ Caching to reduce perceived latency
Together, these turn any external data source into a first-class citizen in your LiveView app.
You’re Not Just Building Frontends
You’re building living reflections of distributed systems.
You orchestrate latency
You surface truth
You earn user trust
Want the Full Pattern Playbook?
Grab the PDF:
Phoenix LiveView: The Pro’s Guide to Scalable Interfaces and UI Patterns
- Async workflows
- External APIs
- Distributed systems
- Real-time feedback
- Design patterns that scale
Use LiveView like a pro — and build UIs that truly reflect reality.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.