[]previous and next page

The resource cache and server rendering

Server rendering and hydration provide a resource cache to useResource through ResourceCacheContext.

For agentsUpdated

Server rendering and hydration provide a resource cache to useResource through ResourceCacheContext. A resource asks the cache for a key: the explicit key option, or a position-derived key. If the cache holds a value for that key, the first load uses it and the fetcher is not called. Later loads (refetch(), a source change) always fetch.

  • renderToString renders a pending Suspense as its fallback.
  • renderToStringAsync runs render passes until the resources the pass asked for have settled, and returns { html, data }, where data maps keys to values.
  • hydrate(code, container, { data }) serves those values to the matching resources, once each, so the browser does not fetch them again.
  • A rejected fetch is not cached.
src/server-data.tsxtsx
import { useResource } from "@shadow-js/core";
import { Suspense } from "@shadow-js/web";
import { renderToString, renderToStringAsync } from "@shadow-js/web/server";

function Name() {
  const name = useResource(async () => "Ada", { key: "name" });
  return <b>{name()}</b>;
}

function App() {
  return (
    <main>
      <Suspense fallback={<i>loading</i>}>
        <Name />
      </Suspense>
    </main>
  );
}

console.log(renderToString(() => <App />)); // the fallback: nothing has resolved yet
const result = await renderToStringAsync(() => <App />);
console.log(result.html);
console.log(JSON.stringify(result.data));
Expected output
<main data-hk="0-0-0"><!--[--><i data-hk="0-0-1-0f-0">loading</i><!--]--></main>
<main data-hk="0-0-0"><!--[--><b data-hk="0-0-1-0-0-0"><!--[-->Ada<!--]--></b><!--]--></main>
{"k:name":"Ada"}

On the client, the same markup hydrates without calling the fetcher:

Streaming (renderToStream) and out-of-order boundaries, deferStream, hydration keys and what must match between server and client are in SSR, hydration and streaming.