A pocketbase collection wrapper for Svelte
- rune based reactivity
- real time sync
- optimistic updates via diff
Installation
pnpm i -D runic-pocketbase-collection
Usage
import PocketBase, { type RecordModel } from "pocketbase"
import { Collection, pbid } from "runic-pocketbase-collection"
// minimal pocketbase setup
let pb = new PocketBase("http://127.0.0.1:8090")
pb.autoCancellation(false)
let tasks = new Collection<RecordModel & { text?: string; done?: boolean }>(pb.collection("tasks"))
// creates 15 character alphanumeric id
let id = pbid()
// create
tasks.update({ [id]: { text: "New task" } })
// read
console.log(tasks.records[id]) // logs: "New task"
// update
tasks.update({ [id]: { done: true } })
// delete
tasks.update({ [id]: null })
// reactivity
let task = $derived(tasks.records[id])
$effect(() => {
console.log(`${task.done ? "done" : "todo"} : ${task.text}`)
})
Task App Example


<script lang="ts">
import { type RecordModel } from "pocketbase"
import { Collection, pbid } from "runic-pocketbase-collection"
let { tasks }: { tasks: Collection<RecordModel & { text?: string; done?: boolean }> } = $props()
let taskList = $derived(Object.values(tasks.records))
let newTaskText = $state("")
</script>
<table>
<thead>
<tr>
<th>
<input
type="checkbox"
indeterminate={taskList.some((task) => task.done) && taskList.some((task) => !task.done)}
checked={taskList.length && taskList.every((task) => task.done)}
onclick={() =>
tasks.update(
Object.fromEntries(
taskList.map((task) => [task.id, { done: !taskList.every((task) => task.done) }])
)
)}
/>
</th>
<th>
{taskList.filter((task) => task.done).length}/{taskList.length} done
</th>
<th>
<button
onclick={() => tasks.update(Object.fromEntries(taskList.map((task) => [task.id, null])))}
>
❌
</button>
</th>
</tr>
</thead>
<tbody>
{#each taskList as task (task.id)}
<tr>
<td>
<input
type="checkbox"
checked={task.done}
onclick={() => tasks.update({ [task.id]: { done: !task.done } })}
/>
</td>
<td>
<input
type="text"
value={task.text}
oninput={(event) => tasks.update({ [task.id]: { text: event.currentTarget.value } })}
/>
</td>
<td><button onclick={() => tasks.update({ [task.id]: null })}>❌</button></td>
</tr>
{/each}
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<input type="text" bind:value={newTaskText} placeholder="New task" />
</td>
<td>
<button
disabled={!newTaskText}
onclick={() => {
tasks.update({ [pbid()]: { text: newTaskText } })
newTaskText = ""
}}
>
✚
</button>
</td>
</tr>
</tfoot>
</table>
<style>
table,
input[type="text"] {
width: 100%;
}
</style>
Development
You'll need to add a pocketbase executable to the database directory.
The following start script assumes you have pnpm / tmux / fish installed.
pnpm i
pnpm run start
# pocketbase now running at http://127.0.0.1:8090/_
# sveltekit app now running at http://localhost:5173