Recipes

Server rendering

Read first-paint named queries once on the server — in-process through the same API authority — dehydrate the rows into the HTML, hydrate the browser store, then hand off to the live wasm engine with no flash.

View as Markdown

For a correct, instant first paint, the server reads each first-paint named query once and returns a dehydrated snapshot embedded in the HTML. The read goes through the same authority the /api/rindle route uses, called in-process (no network hop). The Provider hydrates that seed, then the wasm engine boots and the live subscribe reconciles onto it. No loading flash, no double fetch.

In production — tantaman.github.io

preloadRindle is a strictly server-side module. Each preloaded (name, args) resolves through createAppApi in-process. createServerStore(...).preloadAll(...) assembles the dehydrated cache. A failed read degrades to “no seed for that one query” rather than breaking the page:

// src/ssr.ts — server-only; runs from the route loader
const readInProcess: OneShotQueryFn = async ({ name, args }): Promise<OneShotResult> => {
  const api = createAppApi(resolveRindle(process.env));
  const context: ApiContext<User> = { user: SSR_USER, request: undefined };
  return (await api.handleReadJson({ name, args }, context)) as OneShotResult;
};

/** Preload the given NAMED queries through the authority and return the dehydrated first-paint cache
 *  to embed in the HTML. Call from a route loader (server only). */
export async function preloadRindle(queries: Array<Query<any, any, any>>): Promise<DehydratedState> {
  return createServerStore(schema, { query: readInProcess }).preloadAll(queries, {
    onError: (_query, err) =>
      console.error("[ssr] preload failed; rendering this query without its first-paint seed:", err instanceof Error ? err.message : err),
  });
}

rindle-site/src/ssr.ts L28–42 · tantaman.github.io

The client half of the handoff is a deferred, browser-only boot — createRindleClient dynamically imports the wasm engine so the SSR shell never evaluates it (rindle-site/src/rindle-client.ts L45–75). The client mounts it through the adapter’s Provider.

See also