A layout in a Xenia app folder: a frame around every page in its folder and below.
Layouts nest by folder, not by declaration. A layout.ex applies to every
page in its folder and below, inside the layouts of its ancestor folders.
See Xenia.Routes.Discovery for the folder conventions, and Xenia.Page
for how a page is rendered inside its layouts.
# app/(auth)/layout.ex
defmodule MyAppWeb.AuthLayout do
use MyAppWeb, :html
use Xenia.Layout, on_mount: {MyAppWeb.Auth, :require_user}
def frame(assigns) do
~H"""
<div class="flex">
<aside>{@persistent}</aside>
<main>{@inner_content}</main>
</div>
"""
end
endThe frame
frame/1 is re-rendered on every navigation. It receives the page's
assigns, plus:
@inner_content- the page, already inside the layouts below this one@persistent- this layout's persistent part, ornil; place it where it belongs
The persistent part
A persistent.ex next to the layout.ex is the layout's persistent part:
a LiveView (use Xenia.Persistent) that stays mounted, with its state and
its JavaScript, while the user moves between pages under this layout. The
frame places it with {@persistent}. Persistent parts are rendered side
by side, never one inside another, and survive only within one
live_session (CLAUDE.md §9, experiment S1).
Options
:on_mount-on_mounthooks run when any page under this layout mounts, outermost layout first, likelive_session's. This is where a route group gets its authentication: a folder name grants nothing. Takes a module,{module, argument}, or a list of them.:sitemap-falseleaves every page under this layout out ofXenia.Sitemap, for example pages behind a sign-in. Defaulttrue.
Metadata
A layout can define metadata/1: the title, description and social tags
for the pages under it, which they can override (see Xenia.Metadata).
def metadata(_assigns), do: %{title: "Acme", title_template: "%s · Acme"}
Summary
Callbacks
Renders the layout's frame around @inner_content, the page. @persistent
is this layout's persistent part, or nil.
Metadata for the pages under this layout (see Xenia.Metadata).
Functions
Returns true if module is a Xenia layout.
Returns the on_mount hooks a layout declares, as {module, argument}.
The DOM id of a layout's persistent part. Stable per layout, so every page under the layout renders "the same" sticky LiveView, and LiveView keeps it across navigation.
Whether pages under the layout belong in the sitemap (its :sitemap
option). Raises ArgumentError if module is not a Xenia layout.
Callbacks
@callback frame(assigns :: map()) :: Phoenix.LiveView.Rendered.t()
Renders the layout's frame around @inner_content, the page. @persistent
is this layout's persistent part, or nil.
Metadata for the pages under this layout (see Xenia.Metadata).
Functions
Returns true if module is a Xenia layout.
Returns the on_mount hooks a layout declares, as {module, argument}.
Raises ArgumentError if module is not a Xenia layout.
The DOM id of a layout's persistent part. Stable per layout, so every page under the layout renders "the same" sticky LiveView, and LiveView keeps it across navigation.
Whether pages under the layout belong in the sitemap (its :sitemap
option). Raises ArgumentError if module is not a Xenia layout.