Recipes

TanStack Start

Wire Rindle into TanStack Start with one adapter: `createRindleTanStack` returns the `Provider` that hands off from dehydrated SSR seed to the live wasm engine, and the `loader` that preloads named queries local-first.

View as Markdown

@rindle/tanstack’s createRindleTanStack({ schema, boot, preload }) is the whole binding: it returns a Provider (the dehydrated-seed → live-wasm handoff) and a loader (preload + retain for named queries). You mount the provider once at the router root and use the loader per route.

In the wild — tantaman.github.io

The adapter is set up once. preload is server-only (it reaches the authority in-process — see server rendering); the wrapped loader boots the browser client before any context-scoped named query builds its AST, and defaults until: "present" so routes are local-first:

// src/rindle-tanstack.ts
const integration = createRindleTanStack({
  schema,
  boot: bootClient,
  preload: async (queries) => {
    // Keep the authority/daemon client out of the browser graph. This callback only runs from a
    // server route loader; the adapter's client path calls `bootClient().ensure(...)` instead.
    const { preloadRindle } = await import("./ssr.ts");
    return preloadRindle([...queries]);
  },
});

// Route entry is local-first by default: if the exact view already has useful rows in the wasm
// store, commit the navigation immediately and let its retained server subscription revalidate.
function loader<Context extends RindleLoaderContext = RindleLoaderContext>(
  options: RindleRouteLoaderOptions<Context>,
): RindleRouteLoader<Context> {
  const routeLoader = integration.loader<Context>({ ...options, until: options.until ?? "present" });
  return {
    ...routeLoader,
    handler: async (context) => {
      // Boot first so context-scoped named queries build their local AST from the same principal
      // as the live client.
      if (typeof window !== "undefined") await bootClient();
      return routeLoader.handler(context);
    },
  };
}

export const rindle: typeof integration = { ...integration, loader };

rindle-site/src/rindle-tanstack.ts L15–48 · tantaman.github.io

The provider wraps the router Outlet once at the root (__root.tsx L48–56), and a route reads its query with useRoot:

// src/routes/_shell.$slug.tsx
const [post, { status }] = useRoot(postQuery, slug);

rindle-site/src/routes/_shell.$slug.tsx L43 · tantaman.github.io

See also