A layout's persistent part: a LiveView that stays mounted, with its state
and its JavaScript components, while the user moves between the pages
under its layout. It lives in a persistent.ex next to the layout.ex;
the module name is yours.
# app/(auth)/persistent.ex, next to app/(auth)/layout.ex
defmodule MyAppWeb.ShellLive do
use MyAppWeb, :live_view
use Xenia.Persistent
def mount(_params, _session, socket), do: {:ok, assign(socket, path: nil)}
def handle_navigation(uri, socket) do
{:noreply, assign(socket, path: URI.parse(uri).path)}
end
def render(assigns) do
~H"""
<nav>
<.link navigate="/dashboard" class={@path == "/dashboard" && "active"}>Dashboard</.link>
</nav>
"""
end
endXenia renders it as a sticky LiveView, from the page, at the place its
layout's frame puts @persistent (CLAUDE.md §9, experiment S1). What that
means for you:
- It knows the current page through
handle_navigation/2. A sticky LiveView can't definehandle_params/3: LiveView raises. Xenia callshandle_navigation(uri, socket)once it is connected, and after every navigation and patch, from LiveView'sphx:navigatebrowser event. - It is configured once. It mounts when the first page under its layout renders, and keeps its state until a page outside the layout is shown. Pages can't pass it new parameters; send it a message instead.
- It only persists within one
live_session. Leaving it is a full page load.
Summary
Callbacks
Called with the page's full URL once the persistent part is connected, and after every navigation and patch. Optional.