Renders the LiveView-owned container for a Xenia adapter.
The JavaScript controller creates an adapter-owned child element inside this container.
The component manages:
- The LiveView hook binding, using the registered adapter name.
- The LiveView update binding, set to "ignore".
data-xenia-props, containing JSON-encoded adapter props.data-xenia-events, mapping adapter events to LiveView events.data-xenia-shortcuts, mapping key combinations to LiveView events.- For form fields, an invisible input that carries the field's value.
The adapter keeps it up to date with
ctx.setFormValue/2.
Additional HTML attributes such as class, style, aria-, data-, and phx-click can be passed through.
Binding prefix
Xenia defaults to the LiveView binding prefix "phx-".
To use a custom prefix, configure:
config :xenia, binding_prefix: "pop-"This must match the bindingPrefix option passed to LiveSocket
on the JavaScript side.
Examples
import Xenia.Component
<.xenia
id="editor"
adapter="Monaco"
props={%{language: "elixir", value: %{value: @code, version: @version}}}
events={%{change: "code_changed"}}
class="editor"
style="height: 400px"
/>As a form field, the id and the initial value come from the field:
<.form for={@form} phx-change="validate" phx-submit="save">
<.xenia field={@form[:code]} adapter="Monaco" props={...} />
</.form>Keyboard shortcuts, while focus is inside the component, send LiveView
events (see Xenia.Shortcuts):
<.xenia id="editor" adapter="Monaco" shortcuts={%{"mod+s" => "save"}} />A co-located adapter, defined in the same module, is named with
colocated/1 (see its docs):
<.xenia id="sales" adapter={colocated(".Chart")} props={...} />Loading and errors
<:loading> is shown until the island's first mount finishes, for
example while a large library downloads. The server renders it, so it is
there from the first paint, before any JavaScript runs. Give the
component a height (style or class), and the placeholder fills it.
<:error> is shown instead of the island if its adapter throws: in
mount, a prop handler, a command or a remount. An element inside it
with data-xenia-retry starts the island again. on_crash names a
LiveView event that receives %{"id" => id, "phase" => phase, "message" => message}, where phase is "mount", "update",
"command" or "remount".
<.xenia id="editor" adapter="Monaco" props={...} on_crash="editor_crashed" style="height: 400px">
<:loading>Loading the editor…</:loading>
<:error>
The editor couldn't start. <button type="button" data-xenia-retry>Try again</button>
</:error>
</.xenia>Neither needs any adapter code.
Summary
Functions
The adapter name of a co-located adapter: one defined in this module, in
a <script :type={Phoenix.LiveView.ColocatedHook} name=".Chart"> that
exports adapterHook({...}) from "xenia".
Renders a container for a registered Xenia adapter.
Functions
The adapter name of a co-located adapter: one defined in this module, in
a <script :type={Phoenix.LiveView.ColocatedHook} name=".Chart"> that
exports adapterHook({...}) from "xenia".
<.xenia id="sales" adapter={colocated(".Chart")} props={%{data: @data}} />
<script :type={Phoenix.LiveView.ColocatedHook} name=".Chart">
import Chart from "chart.js/auto"
import { adapterHook } from "xenia"
export default adapterHook({
mount(el, props) {
const canvas = document.createElement("canvas")
el.appendChild(canvas)
return new Chart(canvas, { type: "bar", data: props.data })
},
destroy(chart) {
chart.destroy()
},
})
</script>It expands at compile time to the module-qualified name LiveView gives
that hook, "MyAppWeb.SalesPage.Chart", so two modules can each define
.Chart. <.xenia> can't resolve ".Chart" itself: it runs at render
time and never learns which module called it. So colocated/1 and the
<script> must be in the same module.
The app needs no change per adapter. It calls createHooks once, and
passes the hooks LiveView extracts (import { hooks } from "phoenix-colocated/my_app") to its LiveSocket, as Phoenix's generated
app.js does.
Renders a container for a registered Xenia adapter.
The container remains owned by LiveView. The JavaScript controller manages the child element and adapter lifecycle.
Raises ArgumentError for invalid attributes or values that cannot be JSON-encoded.
Attributes
adapter(:string) (required) - Adapter name registered in JavaScript createHooks(), or colocated(".Name").id(:string) - Stable DOM id used for commands and adapter instances; defaults to the field's id. Defaults tonil.field(Phoenix.HTML.FormField) - Phoenix form field, for example @form[:code]. Defaults tonil.props(:map) - JSON-encodable properties passed to the adapter. Defaults to%{}.events(:map) - Adapter event name => LiveView event name. Defaults to%{}.shortcuts(:map) - Key combination => LiveView event, while focus is inside, e.g. %{"mod+s" => "save"}. Defaults to%{}.on_crash(:string) - LiveView event sent when the adapter throws, with id, phase and message. Defaults tonil.- Global attributes are accepted. Additional HTML attributes not managed by Xenia.
Slots
loading- Shown until the island's first mount finishes.error- Shown if the adapter throws; an element with data-xenia-retry starts it again.