How to Monitor LiveReact with Vigilmon
LiveReact bridges two worlds that rarely talk directly: Phoenix LiveView's server-driven real-time UI and React's vast ecosystem of client components. When you embed a React component inside a LiveView template, state flows in both directions — LiveView pushes updates down via WebSocket and React events propagate back up through pushEvent. The result is powerful, but it introduces a new class of failure that neither standard LiveView monitoring nor React error boundaries catch on their own.
If the WebSocket drops, your React components lose their state sync channel. If a LiveView mount fails, the React tree never renders. If a hydration mismatch occurs after a hot deploy, users see a frozen component with no error message. You need external monitoring to know when these failures are happening in production.
This tutorial covers:
- A health check endpoint that validates both LiveView socket state and React asset serving
- HTTP uptime monitoring with Vigilmon
- Heartbeat monitoring for LiveView processes that drive React components
- Alerts for WebSocket disconnects and hydration failures
Why Monitor LiveReact?
LiveReact failures are subtle because the app appears partially functional:
| Failure mode | Symptom without monitoring |
|---|---|
| LiveView WebSocket disconnect | React component freezes; no server-side updates |
| JavaScript bundle 404 after deploy | React tree never mounts; blank component area |
| Hook registration mismatch | LiveReact hooks silently not called on client |
| Server-side LiveView crash | React component stuck on last known state |
| pushEvent queue overflow | Client events dropped; state diverges from server |
| SSL/CDN misconfiguration | Static assets timeout; React never loads |
Step 1: Health check endpoint
Add a plug that checks database connectivity, LiveView socket acceptability, and static asset serving in one response:
# lib/my_app_web/plugs/health_check.ex
defmodule MyAppWeb.Plugs.HealthCheck do
import Plug.Conn
def init(opts), do: opts
def call(%Plug.Conn{request_path: "/health"} = conn, _opts) do
checks = %{
database: check_database(),
live_view_socket: check_live_view_socket(),
assets: check_assets()
}
status = if Enum.all?(checks, fn {_, v} -> v == :ok end), do: 200, else: 503
conn
|> put_resp_content_type("application/json")
|> send_resp(status, Jason.encode!(%{
status: if(status == 200, do: "ok", else: "degraded"),
checks: checks
}))
|> halt()
end
def call(conn, _opts), do: conn
defp check_database do
case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
{:ok, _} -> :ok
_ -> :error
end
end
defp check_live_view_socket do
# Verify the LiveView endpoint process is alive and accepting connections
endpoint = MyAppWeb.Endpoint
if Process.whereis(endpoint), do: :ok, else: :error
end
defp check_assets do
# Check that the app.js bundle hash file exists (written by mix assets.deploy)
manifest_path = Path.join(:code.priv_dir(:my_app), "static/cache_manifest.json")
if File.exists?(manifest_path), do: :ok, else: :error
end
end
Wire it in before the router so it bypasses authentication plugs:
# lib/my_app_web/endpoint.ex
plug MyAppWeb.Plugs.HealthCheck
plug MyAppWeb.Router
Test locally:
mix phx.server
curl http://localhost:4000/health
# {"status":"ok","checks":{"database":"ok","live_view_socket":"ok","assets":"ok"}}
Step 2: Track React component lifecycle via telemetry
LiveReact emits JavaScript events when hooks connect and disconnect. On the Elixir side, add a LiveView hook that pings a GenServer counter so you can expose the active component count:
# lib/my_app/live_react_monitor.ex
defmodule MyApp.LiveReactMonitor do
use GenServer
def start_link(_), do: GenServer.start_link(__MODULE__, %{active: 0, errors: 0}, name: __MODULE__)
def component_mounted, do: GenServer.cast(__MODULE__, :mounted)
def component_unmounted, do: GenServer.cast(__MODULE__, :unmounted)
def component_error, do: GenServer.cast(__MODULE__, :error)
def stats, do: GenServer.call(__MODULE__, :stats)
@impl true
def init(state), do: {:ok, state}
@impl true
def handle_cast(:mounted, s), do: {:noreply, %{s | active: s.active + 1}}
def handle_cast(:unmounted, s), do: {:noreply, %{s | active: max(0, s.active - 1)}}
def handle_cast(:error, s), do: {:noreply, %{s | errors: s.errors + 1}}
@impl true
def handle_call(:stats, _from, state), do: {:reply, state, state}
end
Add it to your supervision tree:
# lib/my_app/application.ex
children = [
MyApp.Repo,
MyAppWeb.Endpoint,
MyApp.LiveReactMonitor, # ← add
]
Call component_mounted/0 and component_unmounted/0 from your LiveView mount/3 and terminate/2 callbacks in views that host React components.
Update your health plug to include the component stats:
defp check_live_view_socket do
stats = MyApp.LiveReactMonitor.stats()
# Flag if error rate exceeds 10% of total mounts
if stats.errors < 5, do: :ok, else: :degraded
end
Step 3: Set up HTTP monitoring in Vigilmon
Point Vigilmon at your health endpoint:
- Sign up at vigilmon.online
- Click New Monitor → HTTP
- Enter
https://yourdomain.com/health - Set check interval: 1 minute
- Add a keyword check for
"ok"so degraded responses also trigger an alert - Save
Also add a second monitor for your static asset CDN path:
- URL:
https://yourdomain.com/assets/app.js - Expected status:
200 - Keyword check: omit (binary response is fine)
This catches deploy-time CDN purge failures that leave React bundles returning 404.
Step 4: Heartbeat monitoring for LiveView processes
Long-running LiveView processes that push state to React components can hang silently — the process is alive but has stopped processing events. Add a periodic self-check:
# lib/my_app_web/live/dashboard_live.ex
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
@heartbeat_interval :timer.minutes(5)
@heartbeat_url System.get_env("VIGILMON_DASHBOARD_HEARTBEAT_URL")
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
schedule_heartbeat()
MyApp.LiveReactMonitor.component_mounted()
end
{:ok, socket}
end
@impl true
def terminate(_reason, _socket) do
MyApp.LiveReactMonitor.component_unmounted()
:ok
end
@impl true
def handle_info(:heartbeat, socket) do
ping_vigilmon()
schedule_heartbeat()
{:noreply, socket}
end
# ... your existing handle_event and handle_info clauses
defp schedule_heartbeat do
Process.send_after(self(), :heartbeat, @heartbeat_interval)
end
defp ping_vigilmon do
if @heartbeat_url do
Task.start(fn ->
:httpc.request(:get, {String.to_charlist(@heartbeat_url), []}, [{:timeout, 5000}], [])
end)
end
end
end
In Vigilmon, create a Heartbeat Monitor:
- Go to Monitors → New → Heartbeat
- Set the grace period to 10 minutes (2× your heartbeat interval)
- Copy the ping URL into
VIGILMON_DASHBOARD_HEARTBEAT_URL
If your LiveView process crashes and the supervisor can't restart it, the heartbeat stops and Vigilmon alerts you.
Step 5: Key metrics to alert on
| Metric | Alert threshold | Why |
|---|---|---|
| /health HTTP status | Non-200 for 2 consecutive checks | Full stack availability |
| /assets/app.js HTTP status | Non-200 | React bundle unreachable |
| Keyword "degraded" in /health body | Any match | Component error rate elevated |
| Dashboard LiveView heartbeat | Grace period exceeded | LiveView process hung |
| Response time on /health | > 3 s | Database or endpoint congestion |
Step 6: Status page
Add your LiveReact service to a public status page:
- In Vigilmon, go to Status Pages → New
- Add both HTTP monitors (health endpoint and assets) and the LiveView heartbeat
- Name them clearly: "App (Web)", "Static Assets (React)", and "Dashboard (LiveView)"
- Share the URL with your on-call team
Conclusion
LiveReact gives you the best of LiveView server logic and the React component ecosystem, but failures span both the BEAM process layer and the JavaScript client layer. With Vigilmon you get:
- HTTP uptime checks that catch server-side LiveView crashes and CDN asset failures
- Keyword checks that surface degraded states before they become full outages
- Heartbeat monitors that detect hung LiveView processes driving your React components
Start with the health endpoint and a single heartbeat for your most-used LiveView, then expand coverage as your component tree grows.
Get started free at vigilmon.online.