Skip to content

Solid Integration ​

@routier/solid turns a Routier live query into a Solid signal. Signals the query reads are tracked, so there is no dependency list to maintain.

Installation ​

bash
npm install @routier/solid
# peer dependency
npm install solid-js

createLiveQuery ​

ts
function createLiveQuery<T>(
  query: (callback: (result: ResultType<T>) => void) => void | (() => void),
): Accessor<LiveQueryState<T>>;

The accessor returns a discriminated union. Narrow on status before reading data or error:

statusFields
"pending"loading: true
"success"data: T, isSuccess: true
"error"error: Error, isError: true

Tracking ​

The query runs inside an effect. Any signal it reads while building the query becomes a dependency. When one changes, the previous subscription stops, the state returns to pending, and the query runs again. Results from a stopped subscription are ignored.

Cleanup ​

The subscription stops when its owner is disposed: the component, or the root that created it.

Example ​

ts
import { createSignal } from "solid-js";
import { createLiveQuery } from "@routier/solid";
import { store, type Product } from "../../home/inventory";

export const pageSize = 6;

export function createProductPage() {
  const [page, setPage] = createSignal(1);

  const rows = createLiveQuery<Product[]>((onResult) =>
    store.products
      .sort((p) => p.name)
      .skip((page() - 1) * pageSize)
      .take(pageSize)
      .subscribe()
      .toArray(onResult),
  );

  const products = () => {
    const state = rows();
    return state.status === "success" ? state.data : [];
  };

  const error = () => {
    const state = rows();
    return state.status === "error" ? state.error.message : null;
  };

  return { page, setPage, products, error };
}

products and error narrow the state once, so the JSX only reads plain values:

tsx
import { For, Show } from "solid-js";
import { createProductPage } from "./products";

export function ProductGrid() {
  const { page, setPage, products, error } = createProductPage();

  return (
    <>
      <Show when={error()}>{(message) => <p>{message()}</p>}</Show>
      <ul>
        <For each={products()}>{(product) => <li>{product.name}: {product.stock}</li>}</For>
      </ul>
      <button disabled={page() === 1} onClick={() => setPage(page() - 1)}>‹</button>
      <button onClick={() => setPage(page() + 1)}>›</button>
    </>
  );
}

store and Product come from a shared module, the same one the Vue page uses.

Server-side rendering ​

Effects don't run during SolidStart's server render, so the state stays pending there and the query starts in the browser.

Released under the MIT License.