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

Getting started

Quick start

An AtomRpc client, atoms with serialization keys, a router that streams the registry, a loader and a boundary, in a TanStack Start app.

This page builds one route of a TanStack Start app on an Effect RPC contract: an issue tracker's IssueRpcs group, whose GetIssue fails with IssueNotFound and whose every procedure can fail with Unauthorized.

1. The client

AtomRpc.Service makes the RPC client a service with query atoms. During server rendering, the transport calls the RPC handler in the same process; in the browser it posts to the app's /rpc route.

src/issues-client.ts
export class class IssuesClientIssuesClient extends import AtomRpcAtomRpc.
const Service: <IssuesClient>() => <Id, Rpcs, ER, RM>(id: Id, options: {
    readonly group: RpcGroup<Rpcs>;
    readonly protocol: Layer.Layer<Exclude<NoInfer<RM>, Scope>, ER, never> | ((get: AtomContext) => Layer.Layer<Exclude<NoInfer<RM>, Scope>, ER, never>);
    readonly spanPrefix?: string | undefined;
    readonly spanAttributes?: Record<string, unknown> | undefined;
    readonly generateRequestId?: (() => RequestId) | undefined;
    readonly disableTracing?: boolean | undefined;
    readonly makeEffect?: Effect<...> | undefined;
    readonly runtime?: RuntimeFactory | undefined;
}) => AtomRpc.AtomRpcClient<...>
Service
<class IssuesClientIssuesClient>()(
"IssuesClient", {
group: RpcGroup<Rpc<"ListIssues", Struct<{
    cursor: Int;
    sort: withDecodingDefault<Literals<readonly ["newest", "oldest"]>, never>;
    status: withDecodingDefault<Literals<readonly ["all", "open", "in_progress", "closed"]>, never>;
}>, typeof IssuePage, Never, typeof Latency | typeof Authorization, never> | Rpc<"SearchIssues", Struct<{
    query: String;
}>, $Array<typeof Issue>, Never, typeof Latency | typeof Authorization, never> | ... 7 more ... | Rpc<...>>
group
:
const IssueRpcs: RpcGroup<Rpc<"ListIssues", Struct<{
    cursor: Int;
    sort: withDecodingDefault<Literals<readonly ["newest", "oldest"]>, never>;
    status: withDecodingDefault<Literals<readonly ["all", "open", "in_progress", "closed"]>, never>;
}>, typeof IssuePage, Never, typeof Latency | typeof Authorization, never> | Rpc<"SearchIssues", Struct<{
    query: String;
}>, $Array<typeof Issue>, Never, typeof Latency | typeof Authorization, never> | ... 7 more ... | Rpc<...>>
IssueRpcs
,
protocol: Layer.Layer<RpcClient.Protocol, never, never> | ((get: AtomContext) => Layer.Layer<RpcClient.Protocol, never, never>)protocol: import RpcClientRpcClient.
const layerProtocolHttp: (options: {
    readonly url: string;
    readonly transformClient?: <E, R>(client: HttpClient.With<E, R>) => HttpClient.With<E, R>;
    readonly batch?: {
        readonly window: Input;
        readonly maxSize?: number | undefined;
    } | undefined;
}) => Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | HttpClient>
layerProtocolHttp
({ url: stringurl: const rpcUrl: "http://in-process/rpc"rpcUrl }).Pipeable.pipe<Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | HttpClient>, Layer.Layer<RpcClient.Protocol, never, never>>(this: Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | HttpClient>, ab: (_: Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | HttpClient>) => Layer.Layer<RpcClient.Protocol, never, never>): Layer.Layer<...> (+21 overloads)pipe(
import LayerLayer.const provide: <[Layer.Layer<HttpClient, never, never>, Layer.Layer<RpcSerialization.RpcSerialization, never, never>]>(that: [Layer.Layer<HttpClient, never, never>, Layer.Layer<RpcSerialization.RpcSerialization, never, never>]) => <A, E, R>(self: Layer.Layer<A, E, R>) => Layer.Layer<A, E, Exclude<R, RpcSerialization.RpcSerialization | HttpClient>> (+3 overloads)provide([const RpcHttpClient: Layer.Layer<HttpClient, never, never>RpcHttpClient, import RpcSerializationRpcSerialization.const layerJson: Layer.Layer<RpcSerialization.RpcSerialization, never, never>layerJson]), ), }, ) {}

2. The atoms

A query atom with a serializationKey can cross from the server to the browser. Without one, the browser requests it again.

src/issues.ts
// A serialization key lets the server send the result to the browser.
export const issue = (id: number & Brand<"IssueId">id: type IssueId = number & Brand<"IssueId">IssueId) =>
const issue: (id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>
class IssuesClientIssuesClient.
AtomRpcClient<IssuesClient, "IssuesClient", Rpc<"ListIssues", Struct<{ cursor: Int; sort: withDecodingDefault<Literals<readonly ["newest", "oldest"]>, never>; status: withDecodingDefault<...>; }>, typeof IssuePage, Never, typeof Latency | typeof Authorization, never> | ... 8 more ... | Rpc<...>>.query: <"GetIssue">(tag: "GetIssue", payload: {
    readonly id: number & Brand<"IssueId">;
}, options?: {
    readonly headers?: Input | undefined;
    readonly reactivityKeys?: KeysInput | undefined;
    readonly timeToLive?: Input | undefined;
    readonly serializationKey?: string | undefined;
}) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>
query
("GetIssue", { id: number & Brand<"IssueId">id }, { serializationKey?: string | undefinedserializationKey: `${id: number & Brand<"IssueId">id}` });
export const const assignee: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>assignee = (issueId: number & Brand<"IssueId">issueId: type IssueId = number & Brand<"IssueId">IssueId) => class IssuesClientIssuesClient.
AtomRpcClient<IssuesClient, "IssuesClient", Rpc<"ListIssues", Struct<{ cursor: Int; sort: withDecodingDefault<Literals<readonly ["newest", "oldest"]>, never>; status: withDecodingDefault<...>; }>, typeof IssuePage, Never, typeof Latency | typeof Authorization, never> | ... 8 more ... | Rpc<...>>.query: <"GetIssueAssignee">(tag: "GetIssueAssignee", payload: {
    readonly issueId: number & Brand<"IssueId">;
}, options?: {
    readonly headers?: Input | undefined;
    readonly reactivityKeys?: KeysInput | undefined;
    readonly timeToLive?: Input | undefined;
    readonly serializationKey?: string | undefined;
}) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>
query
(
"GetIssueAssignee", { issueId: number & Brand<"IssueId">issueId }, { serializationKey?: string | undefinedserializationKey: `${issueId: number & Brand<"IssueId">issueId}` }, );

3. The router

makeRegistry makes one registry per request on the server and one for the page in the browser. setupRouterAtoms provides it to the app and sends it to the browser with the router's own dehydrated state.

src/router.tsx
export function 
function getRouter(): RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
getRouter
() {
// A request registry on the server, the browser's registry in the browser. const const registry: AtomRegistryregistry =
function makeRegistry(options?: {
    readonly defaultIdleTTL?: number | undefined;
    readonly initialValues?: InitialValues | undefined;
    readonly seed?: Seed | undefined;
}): AtomRegistry
makeRegistry
();
const
const router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
=
createRouter<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>(options: RouterConstructorOptions<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<...>>): RouterCore<...>
createRouter
({
routeTree?: Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined> | undefined
routeTree
,
context: {
    atoms: Loader.Loader;
}
context
: { atoms: Loader.Loaderatoms: import LoaderLoader.const make: (registry: AtomRegistry, options?: Loader.MakeOptions) => Loader.Loadermake(const registry: AtomRegistryregistry) },
defaultPreload?: false | "intent" | "viewport" | "render" | undefineddefaultPreload: "intent", });
setupRouterAtoms<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>(options: RouterAtomsOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, ... 6 more ..., undefined>, "never", false, RouterHistory, Record<...>>>): void
setupRouterAtoms
({
RouterAtomsOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, { atoms: Loader.Loader; }, AnyContext, AnyContext, {}, undefined, ... 4 more ..., undefined>, "never", false, RouterHistory, Record<...>>>.router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
, RouterAtomsOptions<TRouter extends AnyRouter>.registry: AtomRegistryregistry });
return
const router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
;
}

The root route types the loader verbs on the router context and mounts the app's boundaries.

src/routes/__root.tsx
export const 
const Route: RootRoute<Register, undefined, {
    atoms: Loader.Loader;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>
Route
=
createRootRouteWithContext<{
    atoms: Loader.Loader;
}>(): <TRegister, TRouteContextFn, TBeforeLoadFn, TSearchValidator, TLoaderDeps, TLoaderFn, TSSR, TServerMiddlewares>(options?: RootRouteOptions<TRegister, TSearchValidator, {
    atoms: Loader.Loader;
}, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TSSR, TServerMiddlewares, undefined> | undefined) => RootRoute<...>
createRootRouteWithContext
<{ atoms: Loader.Loaderatoms: import LoaderLoader.Loader }>()({
component?: RouteComponent | undefinedcomponent: () => ( // In development, a component that starts an atom no loader started throws. <
const WaterfallDetection: (props: {
    readonly children?: React.ReactNode | undefined;
    readonly mode: WaterfallMode;
}) => React.ReactNode
WaterfallDetection
mode: Modemode="throw">
<const AppBoundary: ErrorBoundaryComponent<"RpcClientError">AppBoundary> <const AuthBoundary: ErrorBoundaryComponent<"Unauthorized" | "RpcClientError">AuthBoundary> <const Outlet: React.MemoExoticComponent<() => import("react").JSX.Element | null>Outlet /> </const AuthBoundary: ErrorBoundaryComponent<"Unauthorized" | "RpcClientError">AuthBoundary> </const AppBoundary: ErrorBoundaryComponent<"RpcClientError">AppBoundary> </
const WaterfallDetection: (props: {
    readonly children?: React.ReactNode | undefined;
    readonly mode: WaterfallMode;
}) => React.ReactNode
WaterfallDetection
>
), });

4. The boundaries

Each boundary catches failures by tag, and passes the rest up the chain: the issue boundary catches IssueNotFound, the sign-in boundary Unauthorized, and the app boundary Effect RPC's RpcClientError, which every procedure fails with when the request doesn't reach the server. See Shared boundaries.

src/boundaries.tsx
// Every procedure fails with RpcClientError when the request doesn't reach
// the server, so the app catches it once.
export const [const AppBoundary: ErrorBoundaryComponent<"RpcClientError">AppBoundary, const useAppBoundary: UseErrorBoundary<"RpcClientError">useAppBoundary] = createErrorBoundary<typeof IssuesClient, "RpcClientError", never>(options: ErrorBoundaryOptions<typeof IssuesClient, "RpcClientError", never>): ErrorBoundary<"RpcClientError">createErrorBoundary({
	ErrorBoundaryOptions<Source, Tags extends TagsOf<ErrorsOf<Source>>, ParentTags extends string>.name: stringname: "App",
	ErrorBoundaryOptions<typeof IssuesClient, "RpcClientError", never>.errors: typeof IssuesClienterrors: class IssuesClientIssuesClient,
	ErrorBoundaryOptions<typeof IssuesClient, "RpcClientError", never>.catchTags: CatchTags<RpcClientError | IssueNotFound | EmptyTitle | EmptyBody | EmptyComment | Unauthorized, "RpcClientError">catchTags: {
		RpcClientError?: ((props: CatchTagProps<RpcClientError>) => React.ReactNode) | undefinedRpcClientError: ({ reset: () => voidreset }) => (
			<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
				Can't reach the server. <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={reset: () => voidreset}>Try again</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
			</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
		),
	},
});
src/boundaries.tsx
// Under the App boundary: every procedure can fail with Unauthorized too.
export const [const AuthBoundary: ErrorBoundaryComponent<"Unauthorized" | "RpcClientError">AuthBoundary, const useAuthBoundary: UseErrorBoundary<"Unauthorized" | "RpcClientError">useAuthBoundary] = createErrorBoundary<typeof IssuesClient, "Unauthorized", "RpcClientError">(options: ErrorBoundaryOptions<typeof IssuesClient, "Unauthorized", "RpcClientError">): ErrorBoundary<"Unauthorized" | "RpcClientError">createErrorBoundary({
	ErrorBoundaryOptions<Source, Tags extends TagsOf<ErrorsOf<Source>>, ParentTags extends string>.name: stringname: "Auth",
	ErrorBoundaryOptions<typeof IssuesClient, "Unauthorized", "RpcClientError">.errors: typeof IssuesClienterrors: class IssuesClientIssuesClient,
	ErrorBoundaryOptions<typeof IssuesClient, "Unauthorized", "RpcClientError">.parent?: ErrorBoundaryComponent<"RpcClientError"> | undefinedparent: const AppBoundary: ErrorBoundaryComponent<"RpcClientError">AppBoundary,
	ErrorBoundaryOptions<typeof IssuesClient, "Unauthorized", "RpcClientError">.catchTags: CatchTags<RpcClientError | IssueNotFound | EmptyTitle | EmptyBody | EmptyComment | Unauthorized, "Unauthorized">catchTags: {
		Unauthorized?: ((props: CatchTagProps<Unauthorized>) => React.ReactNode) | undefinedUnauthorized: ({ reset: () => voidreset }) => (
			<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
				You're signed out. <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={reset: () => voidreset}>Try again</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
			</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
		),
	},
});
src/boundaries.tsx
// Under the Auth boundary: it claims IssueNotFound, and Unauthorized and
// RpcClientError still go up the chain.
export const [const IssueBoundary: ErrorBoundaryComponent<"IssueNotFound" | "Unauthorized" | "RpcClientError">IssueBoundary, const useIssueBoundary: UseErrorBoundary<"IssueNotFound" | "Unauthorized" | "RpcClientError">useIssueBoundary] = createErrorBoundary<typeof IssuesClient, "IssueNotFound", "Unauthorized" | "RpcClientError">(options: ErrorBoundaryOptions<typeof IssuesClient, "IssueNotFound", "Unauthorized" | "RpcClientError">): ErrorBoundary<"IssueNotFound" | "Unauthorized" | "RpcClientError">createErrorBoundary({
	ErrorBoundaryOptions<Source, Tags extends TagsOf<ErrorsOf<Source>>, ParentTags extends string>.name: stringname: "Issue",
	ErrorBoundaryOptions<typeof IssuesClient, "IssueNotFound", "Unauthorized" | "RpcClientError">.errors: typeof IssuesClienterrors: class IssuesClientIssuesClient,
	ErrorBoundaryOptions<typeof IssuesClient, "IssueNotFound", "Unauthorized" | "RpcClientError">.parent?: ErrorBoundaryComponent<"Unauthorized" | "RpcClientError"> | undefinedparent: const AuthBoundary: ErrorBoundaryComponent<"Unauthorized" | "RpcClientError">AuthBoundary,
	ErrorBoundaryOptions<typeof IssuesClient, "IssueNotFound", "Unauthorized" | "RpcClientError">.catchTags: CatchTags<RpcClientError | IssueNotFound | EmptyTitle | EmptyBody | EmptyComment | Unauthorized, "IssueNotFound">catchTags: {
		IssueNotFound?: ((props: CatchTagProps<IssueNotFound>) => React.ReactNode) | undefinedIssueNotFound: ({ error: IssueNotFounderror }) => <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>There's no issue #{error: IssueNotFounderror.id: number & Brand<"IssueId">id}.</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>,
	},
});

5. The route

src/routes/issues.$id.tsx
export const 
const Route: Route<Register, RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$id", "/issues/$id", "/issues/$id", "/issues/$id", undefined, {
    readonly id: number & Brand<"IssueId">;
}, AnyContext, AnyContext, AnyContext, {}, ({ context, params }: LoaderFnContext<Register, RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, ... 7 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, ... 4 more ..., undefined>
Route
=
createFileRoute<"/issues/$id", RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$id", "/issues/$id", "/issues/$id">(path?: "/issues/$id" | undefined): <TRegister, TSearchValidator, TParams, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TChildren, TSSR, TMiddlewares, THandlers>(options?: (ParamsOptions<...> & ... 1 more ... & UpdatableRouteOptions<...>) | undefined) => Route<...>
createFileRoute
("/issues/$id")({
params?: {
    parse?: ParseParamsFn<"/issues/$id", {
        readonly id: number & Brand<"IssueId">;
    }> | undefined;
    priority?: number;
    stringify?: StringifyParamsFn<"/issues/$id", {
        readonly id: number & Brand<"IssueId">;
    }> | undefined;
} | undefined
params
: {
parse?: ParseParamsFn<"/issues/$id", {
    readonly id: number & Brand<"IssueId">;
}> | undefined
parse
: (
params: {
    id: string;
}
params
) => import SchemaSchema.
function decodeUnknownSync<Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>>(schema: Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>, options?: ParseOptions): (input: unknown, options?: ParseOptions) => {
    readonly id: number & Brand<"IssueId">;
}
decodeUnknownSync
(
const IssueParams: Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>
IssueParams
)(
params: {
    id: string;
}
params
),
stringify?: StringifyParamsFn<"/issues/$id", {
    readonly id: number & Brand<"IssueId">;
}> | undefined
stringify
: import SchemaSchema.
const encodeSync: <Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>>(schema: Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>, options?: ParseOptions) => (input: {
    readonly id: number & Brand<"IssueId">;
}, options?: ParseOptions) => {
    readonly id: string;
}
encodeSync
(
const IssueParams: Schema.Struct<{
    readonly id: Schema.compose<Schema.brand<Schema.Int, "IssueId">, Schema.NumberFromString>;
}>
IssueParams
),
},
FilebaseRouteOptionsInterface<Register, RootRoute<Register, undefined, { atoms: Loader; }, AnyContext, AnyContext, {}, undefined, ... 4 more ..., undefined>, ... 12 more ..., undefined>.loader?: Constrain<({ context, params }: LoaderFnContext<Register, RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, ... 7 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, RouteLoaderFn<...> | RouteLoaderObject<...>> | undefined
loader
: async ({
context: {
    atoms: Loader;
}
context
,
params: {
    readonly id: number & Brand<"IssueId">;
}
params
}) => ({
// Starts the assignee; it streams in while the page renders. assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>assignee:
context: {
    atoms: Loader;
}
context
.atoms: Loaderatoms.Loader.prefetch: <(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>(source: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>, issueId: number & Brand<"IssueId">) => Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>prefetch(const assignee: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>assignee,
params: {
    readonly id: number & Brand<"IssueId">;
}
params
.id: number & Brand<"IssueId">id),
// Waits for the issue, so the page renders with it. issue: LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>issue: await
context: {
    atoms: Loader;
}
context
.atoms: Loaderatoms.Loader.ensure: <(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>(source: (id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>, id: number & Brand<"IssueId">) => Promise<LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>>ensure(const issue: (id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>issue,
params: {
    readonly id: number & Brand<"IssueId">;
}
params
.id: number & Brand<"IssueId">id),
}), UpdatableRouteOptionsExtensions.component?: RouteComponent | undefinedcomponent: () => ( <const IssueBoundary: ErrorBoundaryComponent<"IssueNotFound" | "Unauthorized" | "RpcClientError">IssueBoundary> <function IssuePage(): React.JSX.ElementIssuePage /> </const IssueBoundary: ErrorBoundaryComponent<"IssueNotFound" | "Unauthorized" | "RpcClientError">IssueBoundary> ), });
src/routes/issues.$id.tsx
function function IssuePage(): React.JSX.ElementIssuePage() {
	const 
const data: {
    assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>;
    issue: LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>;
}
data
=
const Route: Route<Register, RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$id", "/issues/$id", "/issues/$id", "/issues/$id", undefined, {
    readonly id: number & Brand<"IssueId">;
}, AnyContext, AnyContext, AnyContext, {}, ({ context, params }: LoaderFnContext<Register, RootRoute<Register, undefined, {
    atoms: Loader;
}, AnyContext, ... 7 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, ... 4 more ..., undefined>
Route
.
RouteExtensions<"/issues/$id", "/issues/$id">.useLoaderData: <RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>, unknown, boolean>(opts?: (UseLoaderDataBaseOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    atoms: Loader;
}, AnyContext, ... 7 more ..., undefined>, "never", false, RouterHistory, Record<...>>, "/issues/$id", true, unknown, boolean> & OptionalStructuralSharing<...>) | undefined) => {
    ...;
}
useLoaderData
();
// IssueNotFound, Unauthorized and RpcClientError go to their boundaries, // so no failure is left. const result = useIssueBoundary<SettledResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>(result: SettledResult<Issue, RpcClientError | IssueNotFound | Unauthorized>): Success<Issue, never>useIssueBoundary(useAtomResult<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>(source: (id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>, handle: NoInfer<Handle<(id: number & Brand<"IssueId">) => Atom<AsyncResult<Issue, unknown>>>>): SettledResult<Issue, RpcClientError | ... 1 more ... | Unauthorized>useAtomResult(const issue: (id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>issue,
const data: {
    assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>;
    issue: LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>;
}
data
.issue: LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>issue));
const result: Success<Issue, never>
return ( <React.JSX.IntrinsicElements.article: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>article> <React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>{const result: Success<Issue, never>result.Success<Issue, never>.value: Issuevalue.title: stringtitle}</React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1> <const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense React.SuspenseProps.fallback?: React.ReactNodefallback={<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Loading the assignee…</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>}> <
function Assignee(props: {
    assignee: Handle<typeof assignee>;
}): React.JSX.Element
Assignee
assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>assignee={
const data: {
    assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>;
    issue: LoadedHandle<(id: IssueId) => Atom<AsyncResult<Issue, RpcClientError | IssueNotFound | Unauthorized>>>;
}
data
.assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>assignee} />
</const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense> </React.JSX.IntrinsicElements.article: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>article> ); } // Takes the handle the loader returned, not an id: it reads a request that // already started, and can't start its own. function
function Assignee(props: {
    assignee: Handle<typeof assignee>;
}): React.JSX.Element
Assignee
(
props: {
    assignee: Handle<typeof assignee>;
}
props
: { assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>assignee: interface Handle<S>Handle<typeof const assignee: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>assignee> }) {
const const result: Success<User, never>result = useIssueBoundary<SettledResult<User, RpcClientError | IssueNotFound | Unauthorized>>(result: SettledResult<User, RpcClientError | IssueNotFound | Unauthorized>): Success<User, never>useIssueBoundary(useAtomResult<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>(source: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>, handle: NoInfer<Handle<(issueId: number & Brand<"IssueId">) => Atom<AsyncResult<User, unknown>>>>): SettledResult<...>useAtomResult(const assignee: (issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>assignee,
props: {
    assignee: Handle<typeof assignee>;
}
props
.assignee: Handle<(issueId: IssueId) => Atom<AsyncResult<User, RpcClientError | IssueNotFound | Unauthorized>>>assignee));
return <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>{const result: Success<User, never>result.Success<User, never>.value: Uservalue.name: stringname}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>; }

Open /issues/1: the server waits for the issue, renders the title, and streams the assignee in when it arrives. The browser reads both from its registry without requesting them again. Open /issues/999 and the server sends the IssueNotFound failure instead: the browser renders the issue boundary's message from it, still without a request.