React without TanStack Query
Render-as-you-fetch with use() and Suspense in @trpc12/experimental-react, where cache features are links.
On this page
Two experimental packages read tRPC in React with use() and Suspense, without TanStack Query. Both are experimental as a whole: the package name says so, every export is @stability experimental, and the API may change in any release. For a stable React integration, use TanStack Query.
| Package | Model |
|---|---|
@trpc12/experimental-react | Promises: loaders start queries, components read them with use(). Features are links, opt-in. |
@trpc12/experimental-react-atom | Effect Atoms: query and mutation atoms read with useAtomSuspense (React atoms). |
This page is @trpc12/experimental-react: render-as-you-fetch, after raf-query. A route loader (or an event handler) calls .query(), which starts the request and returns a promise. The component reads that promise with useQuery() and use(). Components don't start requests.
import {
function createReactClient<TSource, const TLinks extends ReadonlyArray<TrpcLink<any, RouterOf<TSource>>>>(opts: ReactClientOptions<TSource, TLinks>): ReactClient<TrpcClient<RouterOf<TSource>, LinksContext<TLinks>, undefined, LinksTypes<TLinks>>>createReactClient,
function standardLinks(opts?: StandardLinksOptions): StandardLinksstandardLinks,
function useQuery<T, E>(promise: QueryPromise<T, E>): QueryPromise<T, E>useQuery,
type type QueryPromise<T, E = unknown> = TrpcPromise<T, E> & Thenable<T>QueryPromise,
} from '@trpc12/experimental-react';
import { function httpLink<TOpts extends HttpLinkOptions>(opts: TOpts): TrpcLink<HttpLinkDecl<TOpts>>httpLink, function routerType<TRouter>(): RouterType<TRouter>routerType } from '@trpc12/trpc/client';
import { const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense, function use<T>(usable: React.Usable<T>): Tuse } from 'react';
import type { type AppRouter = {
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}
AppRouter } from './server';
export const const trpc: ReactClient<TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc = createReactClient<RouterType<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}>, readonly [cache: ...]>(opts: ReactClientOptions<...>): ReactClient<...>
createReactClient({
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { output: Post; errors: TrpcError<...>; services: never; unmapped: never; }>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [cache: ...]>.router: RouterType<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}>
router: routerType<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}>(): RouterType<...>
routerType<type AppRouter = {
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}
AppRouter>(),
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { output: Post; errors: TrpcError<...>; services: never; unmapped: never; }>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [cache: ...]>.links: readonly [cache: TrpcLink<{}, any>, structuralSharing: TrpcLink<{}, any>, refetchOnMount: TrpcLink<{}, any>, refetchOnWindowFocus: TrpcLink<{}, any>, refetchOnReconnect: TrpcLink<{}, any>, invalidateOnMutation: TrpcLink<{}, any>, retry: TrpcLink<{}, any>, TrpcLink<{
form: JsonForm;
provides: "response";
}, any>]
links: [...function standardLinks(opts?: StandardLinksOptions): StandardLinksstandardLinks({ CacheLinkOptions.staleTime?: number | undefinedstaleTime: 10_000 }), httpLink<{
url: string;
}>(opts: {
url: string;
}): TrpcLink<{
form: JsonForm;
provides: "response";
}, any>
httpLink({ url: stringurl: '/trpc' })],
});
// The route's loader starts the request; the navigation doesn't wait for it.
export const const loader: (args: {
params: {
id: string;
};
}) => {
post: QueryPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<TrpcError<"NOT_FOUND", {
id: string;
}> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>, JsonForm>>;
}
loader = (args: {
params: {
id: string;
};
}
args: { params: {
id: string;
}
params: { id: stringid: string } }) => ({
post: QueryPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<TrpcError<"NOT_FOUND", {
id: string;
}> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>, JsonForm>>
post: const trpc: ReactClient<TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: ReactProcedures<ClientRouter<{
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
}, {}, undefined, LinksTypes<...>>>
post.byId: DecorateQuery<[input: {
id: string;
}, opts?: CallOptions<{}> | undefined], {
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<...>>
byId.DecorateQuery<[input: { id: string; }, opts?: CallOptions<{}> | undefined], { id: string; title: string; author: string | null; createdAt: string; }, ClientError<TrpcError<"NOT_FOUND", { id: string; }> | InputValidationError<...>, JsonForm>>.query(input: {
id: string;
}, opts?: CallOptions<{}> | undefined): QueryPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<TrpcError<"NOT_FOUND", {
id: string;
}> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>, JsonForm>>
query({ id: stringid: args: {
params: {
id: string;
};
}
args.params: {
id: string;
}
params.id: stringid }),
});
function function Post(props: {
post: QueryPromise<{
title: string;
}>;
}): React.JSX.Element
Post(props: {
post: QueryPromise<{
title: string;
}>;
}
props: { post: QueryPromise<{
title: string;
}>
post: type QueryPromise<T, E = unknown> = TrpcPromise<T, E> & Thenable<T>QueryPromise<{ title: stringtitle: string }> }) {
const const post: {
title: string;
}
post = use<{
title: string;
}>(usable: React.Usable<{
title: string;
}>): {
title: string;
}
use(useQuery<{
title: string;
}, unknown>(promise: QueryPromise<{
title: string;
}, unknown>): QueryPromise<{
title: string;
}, unknown>
useQuery(props: {
post: QueryPromise<{
title: string;
}>;
}
props.post: QueryPromise<{
title: string;
}>
post));
return <React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>{const post: {
title: string;
}
post.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={<function Spinner(): React.ReactNodeSpinner />}>
<function Post(props: {
post: QueryPromise<{
title: string;
}>;
}): React.JSX.Element
Post post: QueryPromise<{
title: string;
}>
post={const loaderData: {
post: QueryPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<TrpcError<"NOT_FOUND", {
id: string;
}> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>, JsonForm>>;
}
loaderData.post: QueryPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, ClientError<TrpcError<"NOT_FOUND", {
id: string;
}> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>, JsonForm>>
post} />
</const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense>;useQuery finds the cache through the promise, so there is no provider.
Stable promises
Built in, not a link. use() needs the same promise across renders, and these are:
- One promise per query (path and input, with object keys sorted) while it loads, so calls in flight share one request.
- The same promise while the data doesn't change. A new one only when it does: a refetch with new data, or
setData. - Settled promises carry
statusandvalue, souse()renders cached data without suspending.
useQuery(promise) returns the query's current promise and re-renders when it changes. useQueryState(promise) gives isFetching, isStale, error, updatedAt and refetch(), for spinners and retry buttons.
Query links
Without links, the cache does the least: an entry lives while a request is in flight or a mounted useQuery reads it. Everything else is a link, opt-in one by one:
| Link | Does | In standardLinks() |
|---|---|---|
cacheLink({ staleTime, gcTime }) | Keeps unread entries gcTime (5 minutes); fresh data (staleTime, 0) makes no request | yes |
structuralSharingLink() | Refetched data keeps unchanged parts, and unchanged data keeps its promise (no re-render) | yes |
refetchOnMountLink() | A mount on stale data refetches it, except right after the loader's own request | yes |
refetchOnWindowFocusLink() | Refetches stale, mounted queries when the page is visible again | yes |
refetchOnReconnectLink() | The same when the browser comes back online | yes |
retryLink() (core) | Retries failed queries 3 times | yes |
invalidateOnMutationLink() | After a mutation, invalidates queries and resolves once the mounted ones have refetched | yes |
standardLinks() matches TanStack Query's defaults, plus one: like a router after an action, every mutation invalidates queries (all of them, unless invalidates narrows it). Stale data is returned at once and refetched in the background.
Query links are tRPC links: they sit in the same links array, before the link that sends. Write your own with withQueryFeature(link, feature).
Cache methods
await const trpc: ReactClient<TrpcClient<{
post: {
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: ReactProcedures<ClientRouter<{
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>>
post.byId: DecorateQuery<[input: {
id: string;
}, opts?: CallOptions<{}> | undefined], {
...;
}, ClientError<...>>
byId.DecorateQuery<[input: { id: string; }, opts?: CallOptions<{}> | undefined], { id: string; title: string; author: string | null; createdAt: string; }, ClientError<TrpcError<"NOT_FOUND", { id: string; }> | InputValidationError<...>, JsonForm>>.invalidate(input?: {
id: string;
} | undefined): Promise<void>
invalidate({ id: stringid }); // refetches it if it's on screen
await const trpc: ReactClient<TrpcClient<{
post: {
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.function invalidate(filter?: QueryFilter): Promise<void>invalidate('post'); // every query under post.*
const trpc: ReactClient<TrpcClient<{
post: {
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: ReactProcedures<ClientRouter<{
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>>
post.byId: DecorateQuery<[input: {
id: string;
}, opts?: CallOptions<{}> | undefined], {
...;
}, ClientError<...>>
byId.DecorateQuery<[input: { id: string; }, opts?: CallOptions<{}> | undefined], { id: string; title: string; author: string | null; createdAt: string; }, ClientError<TrpcError<"NOT_FOUND", { id: string; }> | InputValidationError<...>, JsonForm>>.setData(input: {
id: string;
}, updater: {
id: string;
title: string;
author: string | null;
createdAt: string;
} | ((prev: {
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined) => {
id: string;
title: string;
author: string | null;
createdAt: string;
})): void
setData({ id: stringid }, (prev: {
...;
} | undefined
prev) => ({ ...prev: {
...;
} | undefined
prev!, title: stringtitle }));
const trpc: ReactClient<TrpcClient<{
post: {
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: ReactProcedures<ClientRouter<{
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>>
post.byId: DecorateQuery<[input: {
id: string;
}, opts?: CallOptions<{}> | undefined], {
...;
}, ClientError<...>>
byId.DecorateQuery<[input: { id: string; }, opts?: CallOptions<{}> | undefined], { id: string; title: string; author: string | null; createdAt: string; }, ClientError<TrpcError<"NOT_FOUND", { id: string; }> | InputValidationError<...>, JsonForm>>.getData(input_0: {
id: string;
}): {
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined
getData({ id: stringid });
await const trpc: ReactClient<TrpcClient<{
post: {
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: ReactProcedures<ClientRouter<{
rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post | undefined;
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<...>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>>
post.rename: {
mutate(input: {
id: string;
title: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined, CallError<{
type: "mutation";
ctx: Context;
meta: {};
input: {
id: string;
title: string;
};
output: Post | undefined;
errors: InputValidationError<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"] | readonly ["title"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>;
}
rename.function mutate(input: {
id: string;
title: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined, CallError<{
type: "mutation";
ctx: Context;
meta: {};
input: {
id: string;
title: string;
};
output: Post | undefined;
errors: InputValidationError<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"] | readonly ["title"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>
mutate({ id: stringid, title: stringtitle }); // mutations and subscriptions are the plain client'sServer rendering
On the server, create one client per request (isServer defaults to typeof window === 'undefined'; a server's cache never drops entries). After the render, dehydrate(trpc) encodes the settled queries with the links' serializer, so Dates survive with danSON; hydrate(trpc, state) writes them into the browser's client before it renders.
const const state: DehydratedQueriesstate = function dehydrate(trpc: HasStore, opts?: HydrationOptions): DehydratedQueriesdehydrate(const serverTrpc: ReactClient<TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}, {}, undefined, LinksTypes<...>>>
serverTrpc); // send it with the HTML
function hydrate(trpc: HasStore, state: DehydratedQueries, opts?: HydrationOptions): voidhydrate(const trpc: ReactClient<TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
list: BuildProcedure<RootDef<Context, {}>, "query", {
output: Post[];
errors: never;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {
...;
};
inputOut: Unset;
errors: TrpcError<...>;
shorts: never;
}>, ZodObject<...>>, "mutation", {
...;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc, const state: DehydratedQueriesstate); // in the browser, before hydrateRoot