# API Reference
Complete reference for all public LiveSvelte APIs.
## Elixir API
### `LiveSvelte.svelte/1`
Renders a Svelte component in a LiveView template.
```heex
<.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.
```elixir
def render(assigns) do
~V"""
<script>
let { count } = $props()
</script>
<p>Count: {count}</p>
"""
end
```
All 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.
```elixir
# 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.
```elixir
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 string
- `id` — DOM id of the wrapper element
- `props` — decoded props map (string keys)
- `slots` — map of slot name → HTML string
- `ssr` — boolean, whether SSR was active
**Example:**
```elixir
{:ok, _view, html} = live(conn, "/counter")
component = get_svelte(html, name: "Counter")
assert component.props["count"] == 0
```
---
### `LiveSvelte.Encoder` Protocol
Protocol for encoding custom structs as JSON props. Implement it directly or use `@derive`:
```elixir
# 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.
```heex
<!-- 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`:
```ts
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.
```ts
import { useLiveSvelte } from "live_svelte"
```
```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 context
- `pushEvent(event, payload, callback?)` — push event to LiveView
- `pushEventTo(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.
```svelte
<script>
import { useLiveEvent } from "live_svelte"
useLiveEvent("item_added", (payload) => {
console.log("New item:", payload)
})
</script>
```
---
### `useLiveConnection()`
Reactive WebSocket connection state.
```svelte
<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.
```svelte
<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 (triggers `handle_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](forms.md) for full documentation.
```ts
import { useLiveForm } from "live_svelte"
```
```svelte
<script>
import { useLiveForm } from "live_svelte"
let { form } = $props()
const { field, fieldArray } = useLiveForm(() => form)
</script>
```
**Parameters:**
- `formFn` — getter function returning the form prop
- `opts?` — `{ changeEvent?, submitEvent?, debounceInMilliseconds? }`
**Returns:**
- `field(name)` — field descriptor with `name`, `value`, `error`, `phx-debounce`
- `fieldArray(name)` — array field with `fields`, `append`, `prepend`, `remove`
---
### `useLiveUpload(uploadConfig, options)`
File upload integration. See [File Uploads](uploads.md) for full documentation.
```ts
import { useLiveUpload } from "live_svelte"
```
```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 getter
- `options` — `{ changeEvent?: string, submitEvent: string }` — `submitEvent` is required
**Returns:**
- `showFilePicker()` — open file picker dialog
- `addFiles(files)` — enqueue files from `File[]` or `DataTransfer` (drag-drop)
- `entries` — `Readable<UploadEntry[]>` store — use `$entries` in templates
- `progress` — `Readable<number>` — overall progress 0–100
- `valid` — `Readable<boolean>` — true when no top-level upload errors
- `submit()` — programmatic form submit
- `cancel(ref?)` — cancel entry by ref string, or all when omitted
- `clear()` — reset file input
- `sync(config)` — merge updated config from server; call in `$effect`
---
### `useEventReply()`
Request-response pattern: push an event and await a reply.
```ts
import { useEventReply } from "live_svelte"
```
```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 a `Promise` that resolves with the server reply
The LiveView must reply using `{:reply, payload, socket}` in `handle_event/3`:
```elixir
def handle_event("save", params, socket) do
{:reply, %{status: "ok"}, socket}
end
```
---
### `Link` Component
Client-side navigation component. Svelte equivalent of Phoenix's `<.link>`.
```svelte
<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](configuration.md) 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 |