Skip to content

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.

typescript
import {
  createQueryStore,
  createMutation,
  createSyncStatusStore,
  getApp,
  applyPresence,
  createCollaboratorsStore,
} from '@korajs/svelte'

Or from the meta-package:

typescript
import { createQueryStore, getApp } from 'korajs/svelte'

Component imports:

svelte
<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 ​

PropTypeRequiredDescription
appKoraAppLikeYesApp instance from createApp().
fallbackSnippetNoShown while initializing.
childrenSnippetNoApp content after ready.

Example ​

svelte
<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().

typescript
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 ​

svelte
<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.

svelte
<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.

svelte
<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).

svelte
<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:

svelte
<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:

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 pathDescription
@korajs/svelteStores, composables, context helpers
@korajs/svelte/KoraProvider.svelteSource (Vite) or precompiled JS (dist/components/)
@korajs/svelte/KoraQuery.svelteReactive query snippet component
@korajs/svelte/KoraRichText.svelteRichtext binding component
@korajs/svelte/KoraStoreProvider.svelteProvider 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.

typescript
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()