The @aboviq/powersync-solid
package provides SolidJS hooks and helpers for use with the JavaScript Web SDK. These hooks are designed to support reactivity, and can be used to automatically re-render SolidJS components when query results update or to access PowerSync connectivity status changes.
Install it:
npm i @aboviq/powersync-solid
# or
yarn add @aboviq/powersync-solid
# or
pnpm add @aboviq/powersync-solid
Follow the instructions in the JavaScript Web SDK docs, then setup the PowerSyncContext
provider:
import { PowerSyncContext } from '@aboviq/powersync-solid';
import { db } from './db'; // <- the PowerSync database instance
import ListsView from './ListsView';
export default function App() {
return (
<PowerSyncContext.Provider value={db}>
<ListsView />
</PowerSyncContext.Provider>
);
}
Then use the hooks and helpers in your components:
// ListsView.tsx
import { useQuery } from '@aboviq/powersync-solid';
export default function ListsView() {
const [lists] = useQuery('SELECT * FROM lists');
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
// Status.tsx
import { useStatus } from '@aboviq/powersync-solid';
export default function Status() {
const status = useStatus();
return (
<p>
<Show when={status().connected} fallback="Offline">
Online
</Show>{' '}
(last sync: {status().lastSyncedAt?.toLocaleDateString() ?? 'n/a'})
</p>
);
}
Note: the useQuery
has the same return type as SolidJS's createResource
hook.
The useQuery
hook is designed to automatically re-render the component when any of the following change:
- PowerSync's connection status (can be easily accessed via the
useStatus
hook) - The query result changes (e.g. when a new record is inserted, updated, or deleted)
- The query itself changes (e.g. when a query signal is used that is updated with new state)
- The query's parameters change (e.g. when any parameter is a signal and it's updated with new state)
import { useQuery } from '@aboviq/powersync-solid';
export default function ListsView() {
const [sortOrder, setSortOrder] = createSignal('ASC');
const [lists] = useQuery(() => `SELECT * FROM lists ORDER BY name ${sortOrder()}`);
const toggleSortOrder = () => {
setSortOrder((sortOrder) => (sortOrder === 'ASC' ? 'DESC' : 'ASC'));
};
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<button onClick={toggleSortOrder}>Toggle sort order</button>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
import { useQuery } from '@aboviq/powersync-solid';
export default function ListsView() {
const [page, setPage] = createSignal(1);
const offet = () => (page() - 1) * 10;
const [lists] = useQuery('SELECT * FROM lists LIMIT 10 OFFSET ?', [offset]);
const previousPage = () => {
setPage((page) => page - 1);
};
const nextPage = () => {
setPage((page) => page + 1);
};
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<button disabled={page() > 1} onClick={previousPage}>
Previous page
</button>
<button disabled={lists()?.length !== 10} onClick={nextPage}>
Next page
</button>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
Example - using Kysely
Set up the Kysely database instance according to the PowerSync Kysely ORM docs.
This will also give you automatic TypeScript type inference for your queries.
import { useQuery } from '@aboviq/powersync-solid';
import { db } from './db'; // <- the file where you have configured your Kysely instance
export default function ListsView() {
const [lists] = useQuery(db.selectFrom('lists').selectAll('lists'));
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
Example - using Kysely with reactive parameters
To use reactive parameters with Kysely, you can pass a function that returns the query to useQuery
.
import { useQuery } from '@aboviq/powersync-solid';
import { db } from './db'; // <- the file where you have configured your Kysely instance
export default function ListsView() {
const [page, setPage] = createSignal(1);
const [lists] = useQuery(() =>
db
.selectFrom('lists')
.selectAll('lists')
.limit(10)
.offset((page() - 1) * 10),
);
const previousPage = () => {
setPage((page) => page - 1);
};
const nextPage = () => {
setPage((page) => page + 1);
};
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<button disabled={page() > 1} onClick={previousPage}>
Previous page
</button>
<button disabled={lists()?.length !== 10} onClick={nextPage}>
Next page
</button>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
If you're not using an ORM, you can still get TypeScript types for your queries by using the useQuery
hook with a type parameter.
import { useQuery } from '@aboviq/powersync-solid';
import type { ListRecord } from './schema'; // <- the file where you have defined your PowerSync database schema
export default function ListsView() {
const [lists] = useQuery<ListRecord>('SELECT * FROM lists');
return (
<div>
<Show when={lists.loading}>
<p>Loading...</p>
</Show>
<Switch>
<Match when={lists.error}>
<span>Error: {lists.error}</span>
</Match>
<Match when={lists()}>
<div>{JSON.stringify(lists())}</div>
</Match>
</Switch>
</div>
);
}
powersync-solid
is licensed under the MIT license. See LICENSE for the full license text.