Live queries
PGlite's live extension re-runs a query whenever the tables it reads change. The module wraps it in composables that return reactive refs and unsubscribe when the component unmounts. Add live to clientExtensions first:
import { live } from '@electric-sql/pglite/live'
export default definePGliteClientConfig({
clientExtensions: { live },
})
useLiveQuery
const { rows, fields } = useLiveQuery<Todo>('SELECT * FROM todos ORDER BY id')
rows and fields are read-only refs, undefined until the first result arrives. Add, toggle or delete below, then open this page in a second tab: the worker is shared, so both lists follow every change.
<script setup lang="ts">
interface Todo {
id: number
title: string
done: boolean
}
const { rows: todos } = useLiveQuery<Todo>('SELECT * FROM todos ORDER BY id')
async function add(title: string) {
const pg = await usePGlite()
await pg.query('INSERT INTO todos (title) VALUES ($1)', [title])
}
async function toggle(todo: Todo) {
const pg = await usePGlite()
await pg.query('UPDATE todos SET done = NOT done WHERE id = $1', [todo.id])
}
async function remove(todo: Todo) {
const pg = await usePGlite()
await pg.query('DELETE FROM todos WHERE id = $1', [todo.id])
}
</script>
The writes do not touch todos: the live query notices the change and updates the ref.
Parameters
The query accepts a string, a ref or a getter; each parameter a plain value, a ref (a computed included) or a getter. When one of them changes, the composable re-subscribes:
const search = ref('')
const pattern = computed(() => `%${search.value}%`)
const { rows } = useLiveQuery<Todo>('SELECT * FROM todos WHERE title ILIKE $1 ORDER BY id', [
pattern,
])
Tagged template
useLiveQuery.sql builds the query and its parameters from a template literal, so interpolated values are sent as parameters, not concatenated:
const minimum = 2
const { rows } = useLiveQuery.sql<Planet>`SELECT * FROM planets WHERE moons >= ${minimum}`
The template form is evaluated once: use the function form with refs for values that change.
useLiveIncrementalQuery
For large result sets, the incremental variant diffs the result by a key column instead of replacing every row:
const { rows } = useLiveIncrementalQuery<Todo>('SELECT * FROM todos ORDER BY id', [], 'id')
The third argument is the column that uniquely identifies a row; like the query, it also accepts a ref or a getter.
Where they can run
The composables subscribe in the browser only and throw during server-side rendering. Call them in the setup of a .client.vue component, or of a component rendered inside <ClientOnly>. They need live in clientExtensions; without it they fail with a message saying so.