Skip to main content
Version: 11.x

useUtils

useUtils is a hook that gives you access to helpers that let you manage the cached data of the queries you execute via @trpc/react-query. These helpers are actually thin wrappers around @tanstack/react-query's queryClient methods. If you want more in-depth information about options and usage patterns for useUtils helpers than what we provide here, we will link to their respective @tanstack/react-query docs so you can refer to them accordingly.

note

This hook was called useContext() until 10.41.0 (and is still aliased for the foreseeable future)

Usage​

useUtils returns an object with all the available queries you have in your routers. You use it the same way as your trpc client object. Once you reach a query, you'll have access to the query helpers. For example, let's say you have a post router with an all query:

server.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.create();
 
const appRouter = t.router({
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
}),
});
 
export type AppRouter = typeof appRouter;
server.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.create();
 
const appRouter = t.router({
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
}),
});
 
export type AppRouter = typeof appRouter;

Now in our component, when we navigate the object useUtils gives us and reach the post.all query, we'll get access to our query helpers!

MyComponent.tsx
tsx
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from './server';
 
const trpc = createTRPCReact<AppRouter>();
 
function MyComponent() {
const utils = trpc.useUtils();
utils.post.all.f;
                  
}
MyComponent.tsx
tsx
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from './server';
 
const trpc = createTRPCReact<AppRouter>();
 
function MyComponent() {
const utils = trpc.useUtils();
utils.post.all.f;
                  
}

Helpers​

These are the helpers you'll get access to via useUtils. The table below will help you know which tRPC helper wraps which @tanstack/react-query helper method. Each react-query method will link to its respective docs/guide:

tRPC helper wrapper@tanstack/react-query helper method
fetchqueryClient.fetchQuery
prefetchqueryClient.prefetchQuery
fetchInfinitequeryClient.fetchInfiniteQuery
prefetchInfinitequeryClient.prefetchInfiniteQuery
ensureDataqueryClient.ensureData
invalidatequeryClient.invalidateQueries
refetchqueryClient.refetchQueries
cancelqueryClient.cancelQueries
setDataqueryClient.setQueryData
setQueriesDataqueryClient.setQueriesData
getDataqueryClient.getQueryData
setInfiniteDataqueryClient.setInfiniteQueryData
getInfiniteDataqueryClient.getInfiniteData
setMutationDefaultsqueryClient.setMutationDefaults
getMutationDefaultsqueryClient.getMutationDefaults
isMutatingqueryClient.isMutating
resetqueryClient.resetQueries

❓ The function I want isn't here!​

@tanstack/react-query has a lot of functions that we haven't put in the tRPC context yet. If you need a function that isn't here, feel free to open a feature request requesting it.

In the meantime, you can import and use the function directly from @tanstack/react-query. We also provide a getQueryKey which you can use to get the correct queryKey on the filters when using these functions.

Proxy client​

In addition to the above react-query helpers, the context also exposes your tRPC proxy client. This lets you call your procedures with async/await without needing to create an additional vanilla client.

tsx
import { useState } from 'react';
import { trpc } from './utils/trpc';
 
function MyComponent() {
const [apiKey, setApiKey] = useState('');
const utils = trpc.useUtils();
 
return (
<form
onSubmit={async (event) => {
const apiKey = await utils.client.apiKey.create.mutate();
setApiKey(apiKey);
}}
>
{/* form content */}
</form>
);
}
tsx
import { useState } from 'react';
import { trpc } from './utils/trpc';
 
function MyComponent() {
const [apiKey, setApiKey] = useState('');
const utils = trpc.useUtils();
 
return (
<form
onSubmit={async (event) => {
const apiKey = await utils.client.apiKey.create.mutate();
setApiKey(apiKey);
}}
>
{/* form content */}
</form>
);
}

Query Invalidation​

You invalidate queries via the invalidate helper. invalidate is actually a special helper given that, unlike the other helpers, it's available at every level of the router map. This means you can either run invalidate on a single query, a whole router, or every router if you want. We get more in detail in the sections below.

Invalidating a single query​

You can invalidate a query relating to a single procedure and even filter based on the input passed to it to prevent unnecessary calls to the back end.

Example code​

tsx
import { trpc } from './utils/trpc';
 
function MyComponent() {
const utils = trpc.useUtils();
 
const mutation = trpc.post.edit.useMutation({
onSuccess(input) {
utils.post.all.invalidate();
utils.post.byId.invalidate({ id: input.id }); // Will not invalidate queries for other id's
},
});
 
// [...]
}
tsx
import { trpc } from './utils/trpc';
 
function MyComponent() {
const utils = trpc.useUtils();
 
const mutation = trpc.post.edit.useMutation({
onSuccess(input) {
utils.post.all.invalidate();
utils.post.byId.invalidate({ id: input.id }); // Will not invalidate queries for other id's
},
});
 
// [...]
}

Invalidating across whole routers​

It is also possible to invalidate queries across an entire router rather than just one query.

Example code​

Backend code
server/routers/_app.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
export const t = initTRPC.create();
 
export const appRouter = t.router({
// sub Post router
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
byId: t.procedure
.input(
z.object({
id: z.string(),
}),
)
.query(({ input }) => {
return {
post: { id: input?.id, title: 'Look me up!' },
};
}),
edit: t.procedure
.input(z.object({ id: z.number(), title: z.string() }))
.mutation(({ input }) => {
return { post: { id: input.id, title: input.title } };
}),
}),
// separate user router
user: t.router({
all: t.procedure.query(() => {
return { users: [{ name: 'Dave Grohl' }, { name: 'Haruki Murakami' }] };
}),
}),
});
server/routers/_app.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
export const t = initTRPC.create();
 
export const appRouter = t.router({
// sub Post router
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
byId: t.procedure
.input(
z.object({
id: z.string(),
}),
)
.query(({ input }) => {
return {
post: { id: input?.id, title: 'Look me up!' },
};
}),
edit: t.procedure
.input(z.object({ id: z.number(), title: z.string() }))
.mutation(({ input }) => {
return { post: { id: input.id, title: input.title } };
}),
}),
// separate user router
user: t.router({
all: t.procedure.query(() => {
return { users: [{ name: 'Dave Grohl' }, { name: 'Haruki Murakami' }] };
}),
}),
});
tsx
import { trpc } from './utils/trpc';
 
function MyComponent() {
const utils = trpc.useUtils();
 
const invalidateAllQueriesAcrossAllRouters = () => {
// 1️⃣
// All queries on all routers will be invalidated
utils.invalidate();
};
 
const invalidateAllPostQueries = () => {
// 2️⃣
// All post queries will be invalidated
utils.post.invalidate();
};
 
const invalidatePostById = () => {
// 3️⃣
// All queries in the post router with input {id:1} invalidated
utils.post.byId.invalidate({ id: 1 });
};
 
// Example queries
trpc.user.all.useQuery(); // Would only be validated by 1️⃣ only.
trpc.post.all.useQuery(); // Would be invalidated by 1️⃣ & 2️⃣
trpc.post.byId.useQuery({ id: 1 }); // Would be invalidated by 1️⃣, 2️⃣ and 3️⃣
trpc.post.byId.useQuery({ id: 2 }); // would be invalidated by 1️⃣ and 2️⃣ but NOT 3️⃣!
 
// [...]
}
tsx
import { trpc } from './utils/trpc';
 
function MyComponent() {
const utils = trpc.useUtils();
 
const invalidateAllQueriesAcrossAllRouters = () => {
// 1️⃣
// All queries on all routers will be invalidated
utils.invalidate();
};
 
const invalidateAllPostQueries = () => {
// 2️⃣
// All post queries will be invalidated
utils.post.invalidate();
};
 
const invalidatePostById = () => {
// 3️⃣
// All queries in the post router with input {id:1} invalidated
utils.post.byId.invalidate({ id: 1 });
};
 
// Example queries
trpc.user.all.useQuery(); // Would only be validated by 1️⃣ only.
trpc.post.all.useQuery(); // Would be invalidated by 1️⃣ & 2️⃣
trpc.post.byId.useQuery({ id: 1 }); // Would be invalidated by 1️⃣, 2️⃣ and 3️⃣
trpc.post.byId.useQuery({ id: 2 }); // would be invalidated by 1️⃣ and 2️⃣ but NOT 3️⃣!
 
// [...]
}

Invalidate full cache on every mutation​

Keeping track of exactly what queries a mutation should invalidate is hard, therefore, it can be a pragmatic solution to invalidate the full cache as a side-effect on any mutation. Since we have request batching, this invalidation will simply refetch all queries on the page you're looking at in one single request.

We have added a feature to help with this:

ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server';
 
export const trpc = createTRPCReact<AppRouter>({
overrides: {
useMutation: {
/**
* This function is called whenever a `.useMutation` succeeds
**/
async onSuccess(opts) {
/**
* @note that order here matters:
* The order here allows route changes in `onSuccess` without
* having a flash of content change whilst redirecting.
**/
 
// Calls the `onSuccess` defined in the `useQuery()`-options:
await opts.originalFn();
 
// Invalidate all queries in the react-query cache:
await opts.queryClient.invalidateQueries();
},
},
},
});
ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server';
 
export const trpc = createTRPCReact<AppRouter>({
overrides: {
useMutation: {
/**
* This function is called whenever a `.useMutation` succeeds
**/
async onSuccess(opts) {
/**
* @note that order here matters:
* The order here allows route changes in `onSuccess` without
* having a flash of content change whilst redirecting.
**/
 
// Calls the `onSuccess` defined in the `useQuery()`-options:
await opts.originalFn();
 
// Invalidate all queries in the react-query cache:
await opts.queryClient.invalidateQueries();
},
},
},
});

Additional Options​

Aside from the query helpers, the object useUtils returns also contains the following properties:

ts
interface ProxyTRPCContextProps<TRouter extends AnyRouter, TSSRContext> {
/**
* The `TRPCClient`
*/
client: TRPCClient<TRouter>;
/**
* The SSR context when server-side rendering
* @default null
*/
ssrContext?: TSSRContext | null;
/**
* State of SSR hydration.
* - `false` if not using SSR.
* - `prepass` when doing a prepass to fetch queries' data
* - `mounting` before TRPCProvider has been rendered on the client
* - `mounted` when the TRPCProvider has been rendered on the client
* @default false
*/
ssrState?: SSRState;
/**
* Abort loading query calls when unmounting a component - usually when navigating to a new page
* @default false
*/
abortOnUnmount?: boolean;
}
ts
interface ProxyTRPCContextProps<TRouter extends AnyRouter, TSSRContext> {
/**
* The `TRPCClient`
*/
client: TRPCClient<TRouter>;
/**
* The SSR context when server-side rendering
* @default null
*/
ssrContext?: TSSRContext | null;
/**
* State of SSR hydration.
* - `false` if not using SSR.
* - `prepass` when doing a prepass to fetch queries' data
* - `mounting` before TRPCProvider has been rendered on the client
* - `mounted` when the TRPCProvider has been rendered on the client
* @default false
*/
ssrState?: SSRState;
/**
* Abort loading query calls when unmounting a component - usually when navigating to a new page
* @default false
*/
abortOnUnmount?: boolean;
}