Svelte API Reference
@korajs/svelte provides Svelte stores, helpers and components for offline-first UIs. The stores and helpers work with Svelte 4 and 5; the .svelte components (KoraProvider, KoraQuery, KoraRichText, KoraStoreProvider) use runes and snippets and need Svelte 5.
import {
createQueryStore,
createMutation,
createSyncStatusStore,
getApp,
applyPresence,
createCollaboratorsStore,
} from '@korajs/svelte'Or from the meta-package:
import { createQueryStore, getApp } from 'korajs/svelte'Component imports:
<script>
import KoraProvider from '@korajs/svelte/KoraProvider.svelte'
import KoraQuery from '@korajs/svelte/KoraQuery.svelte'
</script>KoraProvider
Root layout component that waits for app.ready, sets Kora context, and renders children.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
app | KoraAppLike | Yes | App instance from createApp(). |
fallback | Snippet | No | Shown while initializing. |
children | Snippet | No | App content after ready. |
Example
<script lang="ts">
import KoraProvider from '@korajs/svelte/KoraProvider.svelte'
import App from './App.svelte'
let { kora } = $props()
</script>
<KoraProvider app={kora}>
{#snippet fallback()}Loading...{/snippet}
<App />
</KoraProvider>createQueryStore() / useQuery()
Returns a Svelte Readable store of query results. Subscribe with $store or store.subscribe().
function createQueryStore<T = CollectionRecord>(
query: QueryBuilder<T> | Readable<QueryBuilder<T> | null | undefined>,
options?: {
enabled?: boolean | Readable<boolean>
onError?: (error: Error) => void
},
): Readable<readonly T[]>useQuery is an alias for createQueryStore.
The query and enabled can be plain values or readable stores. Pass a store of the query (for example a derived of your filter) and the rows follow it: the store re-subscribes when the query's descriptor changes, releases the previous subscription, and keeps the previous rows until the new query answers. A store value of null disables the query.
A query that fails (for example a where or orderBy on an unknown field) is reported to onError, or logged with console.error when there is no handler. Use createQueryStateStore to render the error.
Example
<script lang="ts">
import { derived, writable } from 'svelte/store'
import { getApp, createQueryStore } from '@korajs/svelte'
const app = getApp()
const showDone = writable(false)
const todos = createQueryStore(
derived(showDone, (done) => app.todos.where({ completed: done })),
)
</script>
<label><input type="checkbox" bind:checked={$showDone} /> Show done</label>
{#each $todos as todo}
<p>{todo.title}</p>
{/each}createQueryStateStore() / useQueryState()
Same inputs; the store holds { data, error, ready }. error clears when results flow again, and data keeps the last good rows meanwhile.
<script lang="ts">
const state = createQueryStateStore(app.todos.where({ completed: false }))
</script>
{#if $state.error}
<p role="alert">{$state.error.message}</p>
{:else}
{#each $state.data as todo}<p>{todo.title}</p>{/each}
{/if}KoraQuery
Component for queries whose descriptor changes at runtime (reactive filters). Props: query, enabled (default true) and a children snippet that receives the rows.
<KoraQuery query={app.todos.where({ completed: showDone })}>
{#snippet children(data)}
{#each data as todo}
<p>{todo.title}</p>
{/each}
{/snippet}
</KoraQuery>createMutation() / useMutation()
Mutation controller with the same options as the other bindings (onMutate, onRollback, onSuccess, onError, onSettled, onRejected). Returns mutate (fire and forget), mutateAsync, subscribeLoading(fn), subscribeError(fn), subscribeRejected(fn) (each calls fn at once and on every change, and returns an unsubscribe function), the current isLoading, error and rejected, and reset. rejected and onRejected report a later server refusal of a write this mutation made, until its component is destroyed; see Rejected writes.
createSyncStatusStore() / useSyncStatus()
Readable store of SyncStatusInfo. It updates only when the status changes, and also reports heldOperations / heldNodes, localDurability and serverProtocolVersion / protocolDeprecated (see the React reference for their meaning).
<script lang="ts">
import { createSyncStatusStore } from '@korajs/svelte'
const status = createSyncStatusStore()
</script>
<p>{$status.status}</p>getApp() / getCollection()
Context accessors. Must be called after KoraProvider has finished initializing (inside its child tree).
createRichTextBinding() / useRichText()
Binds a t.richtext() field to Yjs. For reactive target changes, prefer KoraRichText.svelte.
applyPresence() / createCollaboratorsStore()
Svelte presence uses an effect-friendly cleanup pattern:
<script lang="ts">
import { applyPresence, createCollaboratorsStore } from '@korajs/svelte'
let user = $state({ name: 'Alice', color: '#e91e63' })
$effect(() => applyPresence(user))
const collaborators = createCollaboratorsStore()
</script>
<ul>
{#each $collaborators as peer}
<li style:color={peer.user.color}>{peer.user.name}</li>
{/each}
</ul>Auth & organizations
Authentication stores and provider components live in @korajs/auth/svelte:
<script>
import AuthProvider from '@korajs/auth/svelte/AuthProvider.svelte'
import OrgProvider from '@korajs/auth/svelte/OrgProvider.svelte'
import { useOrg, usePermission } from '@korajs/auth/svelte'
</script>See Auth API.
Package exports
| Import path | Description |
|---|---|
@korajs/svelte | Stores, composables, context helpers |
@korajs/svelte/KoraProvider.svelte | Source (Vite) or precompiled JS (dist/components/) |
@korajs/svelte/KoraQuery.svelte | Reactive query snippet component |
@korajs/svelte/KoraRichText.svelte | Richtext binding component |
@korajs/svelte/KoraStoreProvider.svelte | Provider for an explicit Store (and sync engine) instead of an app |
Types
Shared binding types are exported from @korajs/core/bindings and re-exported through @korajs/svelte.
useFormDraft()
Keeps a form's input across the reload a new version of the app asks for (opt-in; see App updates and multiple tabs). Same options and rules as the React hook; returns a writable store. userId may be a store or a getter (derived(auth, (a) => (a.isLoading ? undefined : (a.user?.id ?? null)))), or call draft.setUser(id); the field action on a wrapper reads the form control inside it.
const draft = useFormDraft('new-todo', { title: '' }, { fields: ['title'], collection: 'todos' })
onMount(draft.restore)
onDestroy(draft.destroy)
// <input bind:value={$draft.title} use:draft.field={'title'} />
// after a successful submit: draft.clear()
