Convex with TanStack Query
TanStack Query is an excellent, popular library for managing requests to a server.
The
@convex-dev/react-query
library provides
Query Option
functions for use with TanStack Query.
Not all features of the standard Convex React client are available through the TanStack Query APIs but you can use the two alongside each other, dropping into the standard Convex React hooks as necessary.
The TanStack Query adapter is currently a beta feature. If you have feedback or feature requests, let us know on Discord!
This makes subscribing to a Convex query function using the TanStack Query
useQuery
hook look like this:
const { data, isPending, error } = useQuery(convexQuery(api.messages.list, {}));
Instead of the typical polling pattern for API endpoints used with TanStack
Query, the code above receives updates for this api.messages.list
query from
the Convex server reactively. New results for all relevant subscriptions are
pushed to the client where they update at the same time so data is never stale
and there's no need to manually invalidate queries.
Currently only React
Query is
supported via
@convex-dev/react-query
.
Let us know if you would find support for
vue-query, svelte-query, solid-query, or angular-query helpful.
Setup
To get live updates in TanStack Query create a ConvexQueryClient
and connect
it to the TanStack Query
QueryClient.
After installing the adapter library with
npm i @convex-dev/react-query
wire up Convex to TanStack Query like this:
import { ConvexQueryClient } from "@convex-dev/react-query";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
const convexQueryClient = new ConvexQueryClient(convex);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});
convexQueryClient.connect(queryClient);
ReactDOM.createRoot(document.getElementById("root")!).render(
<ConvexProvider client={convex}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ConvexProvider>,
);
Note that when your create your React tree you should both:
- wrap your app in the TanStack Query
QueryClientProvider
so you can use TanStack Query hooks and - wrap your app in the
ConvexProvider
so you can also use normal Convex React hooks
Queries
A live-updating subscription to a Convex
query is as simple as calling TanStack
useQuery
with convexQuery
:
import { useQuery } from "@tanstack/react-query";
import { convexQuery } from "@convex-dev/react-query";
import { api } from "../convex/_generated/api";
export function App() {
const { data, isPending, error } = useQuery(
convexQuery(api.functions.myQuery, { id: 123 }),
);
return isPending ? "Loading..." : data;
}
You can spread the object returned by convexQuery
into an object specifying
additional
arguments of useQuery
.
const { data, isPending, error } = useQuery({
...convexQuery(api.functions.myQuery, { id: 123 }),
initialData: [], // use an empty list if no data is available yet
gcTime: 10000, // stay subscribed for 10 seconds after this component unmounts
});
Mutations
Your app can call Convex mutations by
using the TanStack
useMutation
hook, and setting the mutationFn
property to the result of calling
useConvexMutation
:
import { useMutation } from "@tanstack/react-query";
import { useConvexMutation } from "@convex-dev/react-query";
import { api } from "../convex/_generated/api";
export function App() {
const { mutate, isPending } = useMutation({
mutationFn: useConvexMutation(api.functions.doSomething),
});
return <button onClick={() => mutate({a: "Hello"})}>Click me</button>;
}
useConvexMutation
is just a re-export of the
useMutation
hook from
Convex React.
Differences from using fetch
with TanStack Query
Convex provides stronger guarantees than other methods of fetching data with React Query, so some options and return value properties are no longer necessary.
Subscriptions to Convex queries will remain active after the last component
using useQuery
for a given function unmounts for gcTime
milliseconds. This
value is 5 minutes by default; if this results in unwanted function activity use
a smaller value.
Data provided by Convex is never stale, so the isStale
property of the return
value of useQuery
will always be false. retry
-related options are ignored,
since Convex provides its own retry mechanism over its WebSocket protocol.
refetch
-related options are similarly ignored since Convex queries are always
up to date.