TanStack Start
TanStack Start is a new React web framework with best-in-class typesafe routing.
When used with Convex, TanStack Start provides
- Live-updating queries with React Query (the React client for TanStack Query)
- Subscription session resumption, from SSR to live on the client
- Loader-based preloading and prefetching
- Consistent logical query timestamp during SSR
- Opt-in component-local SSR
and more!
This page describes the recommended way to use Convex with TanStack Start, via React Query. The standard Convex React hooks work also with TanStack Start without React Query, as do the React Query hooks without TanStack Start! But using all three is a sweet spot.
TanStack Start is a new React framework currently in alpha. You can try it today but there will probably be breaking changes made to it before a stable release.
Getting started
Follow the TanStack Start Quickstart to add Convex to a new TanStack Start project.
Using Convex with React Query
You can read more about React Query hooks, but a few highlights relevant to TanStack Start.
Staying subscribed to queries
Convex queries in React Query continue to receive updates after the last
component subscribed to the query unmounts. The default for this behavior is 5
minutes and this value is configured with
gcTime
.
This is useful to know when debugging why a query result is already loaded: for client side navigations, whether a subscription is already active can depend on what pages were previously visited in a session.
Using Convex React hooks
Convex React hooks like
usePaginatedQuery
can be used
alongside TanStack hooks. These hooks reference the same Convex Client so
there's still just one set of consistent query results in your app when these
are combined.
Server-side Rendering
Using TanStack Start and Query with Convex makes it particularly easy to
live-update Convex queries on the client while also
server-rendering
them.
useSuspenseQuery()
is the simplest way to do this:
const { data } = useSuspenseQuery(convexQuery(api.messages.list, {}));
Consistent client views
In the browser all Convex query subscriptions present a consistent, at-the-same-logical-timestamp view of the database: if one query result reflects a given mutation transaction, every other query result will too.
Server-side rendering is usually a special case: instead of a stateful WebSocket session, on the server it's simpler to fetch query results ad-hoc. This can lead to inconsistencies analogous to one REST endpoint returning results before a mutation ran and another endpoint returning results after that change.
In TanStack Start, this issue is avoided by sending in a timestamp along with each query: Convex uses the same timestamp for all queries.
Loaders
To make client-side navigations faster you can add a loader to a route. By default, loaders will run when mousing over a link to that page.
export const Route = createFileRoute('/posts')({
loader: async (opts) => {
await opts.context.queryClient.ensureQueryData(
convexQuery(api.messages.list, {}),
);
};
component: () => {
const { data } = useSuspenseQuery(convexQuery(api.messages.list, {}));
return (
<div>
{data.map((message) => (
<Message key={message.id} post={message} />
))}
</div>
);
},
})