Defer a LiveView's mount/3 and handle_params/3 until the socket connects.
A LiveView's mount/3 runs twice on first load: once during the static HTTP
render (the "dead render", where connected?(socket) == false), and again in
the spawned LiveView process once the WebSocket connects. handle_params/3
runs in both phases too. For pages that don't need SSR/SEO, doing the expensive
work twice — once for HTML that is about to be thrown away — is wasted effort.
LiveConnected skips the user's mount/handle_params bodies on the dead
render and shows a skeleton instead, running the real work only once the socket
connects.
Usage
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
use LiveConnected
def mount(_params, _session, socket) do
{:ok, assign(socket, :stats, Analytics.expensive_dashboard())}
end
def render(assigns) do
~H"<Dashboard.stats stats={@stats} />"
end
# optional — omit for a generic skeleton
def loading(assigns), do: ~H"<Dashboard.skeleton />"
endOrdering matters
use LiveConnected must come after use MyAppWeb, :live_view. That
ordering ensures the ~H sigil and LiveView's callback defaults are in scope
when the wrappers are compiled.
How it works
On the dead render the mount wrapper assigns live_connected?: false and
returns immediately without calling your mount. The handle_params and
render wrappers read that flag: handle_params no-ops, and render calls
loading/1 instead of your render/1. On connect the wrapper assigns
live_connected?: true and calls super, so everything runs normally.
All phase state keys off the single :live_connected? assign. The
handle_params and render wrappers read it defensively
(Map.get(..., :live_connected?, true)), so a missing flag fails toward
"run normally" rather than crashing.
Opting out
Pass enabled: false to behave like a normal LiveView (runs everything in both
phases). This is the escape hatch for when a page later needs SSR/SEO.
use LiveConnected, enabled: falseCaveats
Deferring handle_params moves status codes and redirects to the connected
phase. A "resource missing → 404" or an ownership redirect now happens on
connect, so the static response is a 200 with a skeleton and the user sees a
brief flash of skeleton before the bounce. For the SEO-irrelevant pages this
targets that is acceptable. When you need a correct dead-render status, use the
cheap-guard pattern: branch on @live_connected? inside your own callback and
run a trivial existence/authz check in both phases, deferring only the
expensive load. See the README for an example.
Summary
Functions
Injects the deferral wrappers into the calling LiveView.