The promise of Phoenix LiveView is real-time UI without the JavaScript mess — rich interactivity with minimal client code. But what happens when the real world gets in the way?
When a user switches tabs and comes back.
When Wi-Fi drops for a few seconds.
When a mobile connection hiccups mid-form submission.
These aren’t edge cases. They’re reality.
And if you want your LiveView app to feel like a native experience, it can’t just work when everything’s perfect. It has to survive interruptions. It has to reconnect without breaking the UI, resynchronize state without losing context, and gracefully degrade when recovery isn’t possible.
That’s where LiveView’s reconnect and recovery mechanisms come into play — not as marketing features, but as essential parts of building real-world software.
The Persistent WebSocket
The magic begins with the persistent WebSocket connection that powers every LiveView session.
As soon as a user lands on a LiveView-enabled page, a WebSocket is opened and maintained.
Unlike HTTP (which forgets everything on each request), this connection is long-lived. That’s great for interactivity — until it’s not.
If the socket is severed, even for a moment:
- Forms may be halfway completed.
- Background processes may be syncing state.
- UI components may be mid-transition.
The last thing you want is for users to lose work or context.
Silent Reconnection
LiveView handles this with quiet brilliance.
- The client maintains a heartbeat with the server.
- If the connection drops, it waits and retries.
- No page reload. No lost assigns.
- The DOM is preserved.
- Once the socket is back, only the minimal diff is sent.
This is what makes LiveView feel native — especially on mobile.
Developer Responsibility: Managing State
From your perspective as a developer, reconnection is automatic, but state management is not.
LiveView’s server process holds the canonical state for each user session.
Any local JS or temporary UI that isn't synced with the LiveView process will be lost during reconnection.
If the process is still alive: ✅ everything resumes.
If the process died (timeout, crash, node restart): ⚠️ a new LiveView is spun up.
This is where idempotent recovery comes in.
- Don’t assume a new visit is a clean slate.
- Users might be reconnecting mid-action.
- For
mount/3
, handle cached data and pass flags to avoid redundant work. - Repopulate forms from saved state.
- For ephemerals (like flash messages), think about whether they should return on reconnect.
Using Session and Connected State
You can:
- Store info in the session at initial connect (auth tokens, preferences).
- Use
connected?/1
inmount/3
to distinguish cold visits from hot reconnects.
if connected?(socket) do
# Live reconnect
else
# First time visit
end
Use this distinction to:
- Skip redundant operations
- Defer animations
- Avoid duplicate tracking
When Reconnection Fails
If the socket cannot reconnect:
- LiveView eventually falls back to a hard reload.
You can still make this graceful:
- Store form data in
localStorage
- Use query params to preserve context
- Use persistent components (
LiveComponent
) to isolate state
Custom JS Hooks for Connection Status
You can extend the default JS hook to react to connection state.
Examples:
- Show a “Reconnecting…” banner
- Disable form inputs while disconnected
- Restore interactivity after reattachment
mounted() {
this.handleEvent("lv:disconnected", () => {
showMessage("You’ve been disconnected. Trying to reconnect...");
});
this.handleEvent("lv:reconnected", () => {
hideMessage();
});
}
This real-time UX honesty builds trust.
It’s Not Always the Network
Not all disconnections are flaky connections.
- Users close laptops
- Sleep their phones
- Switch Wi-Fi networks
These aren’t failures. They’re use cases.
Your app must adapt.
- Do your GenServers clean up after abandoned sessions?
- Do you use Presence to track user state?
- Do you debounce events that fire on reconnect?
- Do you handle zombie sessions?
Resilience Is a UX Feature
Resilience isn’t just about the BEAM keeping your server alive.
It’s about your app being context-aware:
- A disconnected tab shouldn’t mean lost progress.
- A reconnected session shouldn’t mean duplicate work.
- A LiveView app should live through interruption.
You’re Building More Than Views
When you think this way, you realize something powerful:
You’re not just building views.
You’re building long-lived sessions, intelligent recovery flows, and real-time processes that fail, restart, and recover without drama.
That’s not front-end polish.
That’s backend muscle.
Want to Go Deeper?
If you're serious about using Phoenix LiveView like a pro, I’ve put together a detailed PDF guide:
Phoenix LiveView: The Pro’s Guide to Scalable Interfaces and UI Patterns
- 20-page deep dive
- Advanced features
- Architectural best practices
- Reusable patterns
- Real-world production advice
Whether creating a new interface or refactoring an old one, this guide will save time and help you make the most of LiveView’s capabilities.
Top comments (0)