Skip to content

SolidJS helpers, components and hooks for PowerSync

License

Notifications You must be signed in to change notification settings

aboviq/powersync-solid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@aboviq/powersync-solid

@aboviq/powersync-solid

pnpm

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.

Quick start

Install it:

npm i @aboviq/powersync-solid
# or
yarn add @aboviq/powersync-solid
# or
pnpm add @aboviq/powersync-solid

Usage

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.

Watched Queries

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)

Example - a reactive query

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>
  );
}

Example - reactive parameters

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>
  );
}

Using with an ORM

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>
  );
}

TypeScript types without an ORM

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>
  );
}

License

powersync-solid is licensed under the MIT license. See LICENSE for the full license text.