Skip to content

raf-effect-atom and effect-atom-boundary are on npm. Both are pre-1.0, so a minor release can still break them. Install them

raf-effect-atom

API reference

raf-effect-atom/tanstack-router

Connects the loader verbs to TanStack Router and TanStack Start.

getRouter runs once per request on the server and once in the browser. It makes the registry with makeRegistry, puts Loader.make(registry) on the router context for the loaders, and calls setupRouterAtoms, which provides the registry to the app and sends it from the server to the browser through the router's own dehydration.

Example

import { createRouter } from "@tanstack/react-router"
import { Loader } from "raf-effect-atom"
import { makeRegistry, setupRouterAtoms } from "raf-effect-atom/tanstack-router"

export function getRouter() {
  const registry = makeRegistry()
  const router = createRouter({
    routeTree,
    context: { atoms: Loader.make(registry) }
  })
  setupRouterAtoms({ router, registry })
  return router
}
import { makeRegistry, setupRouterAtoms } from "raf-effect-atom/tanstack-router";

DehydratedRouterAtoms

interfacemodelssince 0.1.0Source

interface DehydratedRouterAtoms {
  readonly "raf-effect-atom": ReadonlyArray<EffectHydration.DehydratedAtom>;
}

The key setupRouterAtoms adds to the router's dehydrated state.

RouterAtomsOptions

interfacemodelssince 0.1.0Source

interface RouterAtomsOptions<TRouter extends AnyRouter> {
  /** The registry the router context's loader verbs run on. */
  readonly registry: AtomRegistry.AtomRegistry;
  readonly router: TRouter;
}

Options for setupRouterAtoms.

makeRegistry

constconstructorssince 0.1.0Source

const makeRegistry: (options?: {
  readonly defaultIdleTTL?: number | undefined;
  readonly initialValues?: RequestRegistry.InitialValues | undefined;
  readonly seed?: RequestRegistry.Seed | undefined;
}) => AtomRegistry.AtomRegistry;

Creates the registry for one getRouter call: a request registry on the server, and in the browser a registry like @effect/atom-react's RegistryProvider makes.

Details

  • The server is anywhere without a window. To decide otherwise, make the registry with RequestRegistry.make on the server and AtomRegistry.make in the browser.
  • defaultIdleTTL is how many milliseconds the browser's registry keeps an atom no component reads, 400 by default, as @effect/atom-react's default registry does. An atom's own Atom.setIdleTTL or Atom.keepAlive wins. The server's request registry lives for one request, so it has none.
  • initialValues are values both registries start with, as AtomRegistry.make's initialValues, such as the session the server verified or a readable atom that provides the request's fetch. The server's request registry keeps those atoms mounted until it's disposed. The browser's registry drops an atom no component reads, value included, so make one whose value must outlive its readers Atom.keepAlive.
  • seed runs on the server's request registry before the loaders start atoms in it, as RequestRegistry.make's does: set what the request's atoms need, such as its cookie. It can be async. The browser's registry isn't seeded.

Example

import { getRequestHeader } from "@tanstack/react-start/server"
import { makeRegistry } from "raf-effect-atom/tanstack-router"

const registry = makeRegistry({
  // Keeps an issue the user navigated away from for a minute.
  defaultIdleTTL: 60_000,
  // In-process RPC calls send the page request's cookie.
  seed: (registry) => registry.set(requestCookie, getRequestHeader("cookie"))
})

setupRouterAtoms

constintegrationssince 0.1.0Source

const setupRouterAtoms: <TRouter extends AnyRouter>(
  options: RouterAtomsOptions<TRouter>,
) => void;

Provides a registry to a TanStack router's app and streams it from the server to the browser.

Details

  • Wraps the app in RegistryContext.Provider through the router's Wrap option, inside any Wrap it has already.
  • On the server, adds Hydration.dehydrate(registry) to the router's dehydrated state once the loaders have run, so results still loading cross as promises on the router's stream, and disposes the registry when the response finishes.
  • In the browser, hydrates the registry from that state before the app renders.

Gotchas

The server's registry must be a request registry and the browser's must not, so it throws otherwise: a registry shared between requests would send one user's results to another. Only atoms in the registry when the loaders finish are sent; one first read while rendering runs again in the browser.