Complete reference for all public LiveSvelte APIs.
Elixir API
LiveSvelte.svelte/1
Renders a Svelte component in a LiveView template.
<.svelte name="Counter" props={%{count: @count}} socket={@socket} />Attributes:
| Attribute | Type | Default | Required | Description |
|---|---|---|---|---|
name | string | — | ✓ | Component name (filename without .svelte, relative to assets/svelte/) |
props | map | %{} | Props to pass to the component | |
socket | map | nil | LiveView socket — required when ssr: true | |
id | string | auto | Stable DOM id override | |
key | any | nil | Identity key for DOM id generation in loops | |
class | string | nil | CSS class on the wrapper div | |
ssr | boolean | true | Enable SSR for this component | |
diff | boolean | true | Enable props diffing (requires enable_props_diff: true in config) | |
:loading slot | Content shown while component loads (only with ssr={false}) | |||
:inner_block slot | Inner content (passed to Svelte as a slot) |
Name examples:
Counter.svelte → name="Counter"
forms/UserForm.svelte → name="forms/UserForm"~V Sigil
Inline Svelte template as a LiveView render macro.
def render(assigns) do
~V"""
<script>
let { count } = $props()
</script>
<p>Count: {count}</p>
"""
endAll LiveView assigns are automatically available as props. The template is written to assets/svelte/_build/MyModule.svelte at compile time.
LiveSvelte.Components
Auto-generated shorthand component functions based on discovered .svelte files.
# In web module html_helpers:
use LiveSvelte.Components
# In templates — instead of <.svelte name="Counter" ...>:
<.Counter count={@count} socket={@socket} />Counter.svelte → <.Counter>, forms/UserForm.svelte → <.forms_UserForm> (slashes converted to underscores).
LiveSvelte.Test.get_svelte/1,2
Inspect Svelte component props from HTML in tests.
import LiveSvelte.Test
# Get first component in HTML
component = get_svelte(html)
# Get component by name
component = get_svelte(html, name: "Counter")
# Get component by DOM id
component = get_svelte(html, id: "Counter-1")
# Get directly from a LiveView
{:ok, view, _html} = live(conn, "/counter")
component = get_svelte(view, name: "Counter")Returns a map with:
name— component name stringid— DOM id of the wrapper elementprops— decoded props map (string keys)slots— map of slot name → HTML stringssr— boolean, whether SSR was active
Example:
{:ok, _view, html} = live(conn, "/counter")
component = get_svelte(html, name: "Counter")
assert component.props["count"] == 0LiveSvelte.Encoder Protocol
Protocol for encoding custom structs as JSON props. Implement it directly or use @derive:
# Simple derive — exposes all public fields
@derive LiveSvelte.Encoder
defstruct [:id, :name]
# Restricted derive — only expose listed fields
@derive {LiveSvelte.Encoder, only: [:id, :name, :email]}
defstruct [:id, :name, :email, :password_hash]
# Excluded fields derive
@derive {LiveSvelte.Encoder, except: [:password_hash]}
defstruct [:id, :name, :email, :password_hash]Without @derive, passing a struct as a prop will raise an error.
LiveSvelte.Reload / vite_assets/1
With phoenix_vite: The layout uses PhoenixVite.Components.assets; ensure config/dev.exs has the endpoint’s static_url: [host: "localhost", port: 5173] and watchers: [..., vite: {PhoenixVite.Npm, :run, [:vite, ~w(dev)]}] so the Vite dev server runs and HMR works. The Igniter installer adds these.
Without phoenix_vite: Use LiveSvelte.Reload.vite_assets/1 in your layout. HMR helper for development; includes the Vite dev server client script.
<!-- In root layout, development only -->
<%= if Application.get_env(:live_svelte, :ssr_module) == LiveSvelte.SSR.ViteJS do %>
<LiveSvelte.Reload.vite_assets path="/assets/js/app.js" />
<% end %>JavaScript API
getHooks(Components)
Entry point. Returns a hooks map to pass to LiveSocket:
import { getHooks } from "live_svelte"
import Components from "virtual:live-svelte-components"
const liveSocket = new LiveSocket("/live", Socket, {
hooks: getHooks(Components),
params: { _csrf_token: csrfToken }
})useLiveSvelte()
Access the Phoenix hook context from any LiveSvelte-mounted component.
import { useLiveSvelte } from "live_svelte"<script>
import { useLiveSvelte } from "live_svelte"
const { pushEvent, pushEventTo, live } = useLiveSvelte()
function save(data) {
pushEvent("save", data)
}
function saveWithReply(data) {
pushEvent("save", data, (reply) => console.log(reply))
}
</script>Returns:
live— raw Phoenix hook contextpushEvent(event, payload, callback?)— push event to LiveViewpushEventTo(target, event, payload, callback?)— push event to specific LiveView
useLiveEvent(event, callback)
Subscribe to a server-sent LiveView event. Automatically cleans up on component destroy.
<script>
import { useLiveEvent } from "live_svelte"
useLiveEvent("item_added", (payload) => {
console.log("New item:", payload)
})
</script>useLiveConnection()
Reactive WebSocket connection state.
<script>
import { useLiveConnection } from "live_svelte"
const conn = useLiveConnection()
</script>
{#if !conn.connected}
<div class="banner">Reconnecting...</div>
{/if}Returns:
connected—boolean, reactive
useLiveNavigation()
Client-side LiveView navigation.
<script>
import { useLiveNavigation } from "live_svelte"
const { patch, navigate } = useLiveNavigation()
</script>
<button onclick={() => patch("?page=2")}>Next page</button>
<button onclick={() => navigate("/other")}>Navigate</button>Returns:
patch(hrefOrParams, opts?)— patch current LiveView (triggershandle_params/3)navigate(href, opts?)— navigate to a new LiveView
Both accept { replace: true } to use history.replaceState.
useLiveForm(formFn, opts?)
Reactive form binding with Ecto changeset support. See Forms and Validation for full documentation.
import { useLiveForm } from "live_svelte"<script>
import { useLiveForm } from "live_svelte"
let { form } = $props()
const { field, fieldArray } = useLiveForm(() => form)
</script>Parameters:
formFn— getter function returning the form propopts?—{ changeEvent?, submitEvent?, debounceInMilliseconds? }
Returns:
field(name)— field descriptor withname,value,error,phx-debouncefieldArray(name)— array field withfields,append,prepend,remove
useLiveUpload(uploadConfig, options)
File upload integration. See File Uploads for full documentation.
import { useLiveUpload } from "live_svelte"<script>
import { useLiveUpload } from "live_svelte"
let { uploads } = $props()
const { showFilePicker, entries, submit, cancel, sync } = useLiveUpload(
uploads.avatar,
{ changeEvent: "validate", submitEvent: "submit" }
)
$effect(() => sync(uploads.avatar))
</script>Parameters:
uploadConfig— the upload config object (e.g.uploads.avatar), passed directly not as a getteroptions—{ changeEvent?: string, submitEvent: string }—submitEventis required
Returns:
showFilePicker()— open file picker dialogaddFiles(files)— enqueue files fromFile[]orDataTransfer(drag-drop)entries—Readable<UploadEntry[]>store — use$entriesin templatesprogress—Readable<number>— overall progress 0–100valid—Readable<boolean>— true when no top-level upload errorssubmit()— programmatic form submitcancel(ref?)— cancel entry by ref string, or all when omittedclear()— reset file inputsync(config)— merge updated config from server; call in$effect
useEventReply()
Request-response pattern: push an event and await a reply.
import { useEventReply } from "live_svelte"<script>
import { useEventReply } from "live_svelte"
const { push } = useEventReply()
async function save(data) {
const result = await push("save", data)
console.log("Server replied:", result)
}
</script>Returns:
push(event, payload)— returns aPromisethat resolves with the server reply
The LiveView must reply using {:reply, payload, socket} in handle_event/3:
def handle_event("save", params, socket) do
{:reply, %{status: "ok"}, socket}
endLink Component
Client-side navigation component. Svelte equivalent of Phoenix's <.link>.
<script>
import { Link } from "live_svelte"
</script>
<Link href="/other-page">Go to other page</Link>
<Link href="/other-page" replace={true}>Replace history</Link>Telemetry Events
| Event | Measurements | Metadata | Description |
|---|---|---|---|
[:live_svelte, :ssr, :start] | %{system_time: integer} | %{component: name} | SSR render begins |
[:live_svelte, :ssr, :stop] | %{duration_microseconds: integer} | %{component: name} | SSR render completes |
[:live_svelte, :ssr, :exception] | %{system_time: integer} | %{component: name, reason: term} | SSR render fails |
Configuration Keys
See Configuration for full details.
| Key | Default | Description |
|---|---|---|
config :live_svelte, :ssr | true | Global SSR enable/disable |
config :live_svelte, :ssr_module | LiveSvelte.SSR.NodeJS | SSR module |
config :live_svelte, :json_library | LiveSvelte.JSON | JSON encoder |
config :live_svelte, :enable_props_diff | true | Props diffing system |
config :live_svelte, :gettext_backend | nil | Gettext for form errors |
config :live_svelte, :vite_host | "http://localhost:5173" | Vite dev server URL |