#I'm using tanstack router and convex, how can I fetch data from a loader?
26 messages · Page 1 of 1 (latest)
Hi @gusty fiber. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
To fetch data from a loader when using TanStack Router and Convex, you should use the loader function in your route definition. Inside the loader, you can use the TanStack Query client to prefetch or fetch data before the component renders. This is typically done using the ensureQueryData or fetchQuery methods with the convexQuery helper.
Here’s a common pattern:
import { createFileRoute } from "@tanstack/react-router";
import { convexQuery } from "@convex-dev/react-query";
import { api } from "../../convex/_generated/api";
export const Route = createFileRoute("/posts")({
loader: async (opts) => {
// Prefetch the data so it's ready when the component renders
await opts.context.queryClient.ensureQueryData(
convexQuery(api.messages.list, {})
);
},
component: () => {
// Use useSuspenseQuery or useQuery in your component for reactivity
// ...
},
});
- The loader runs during SSR and on client-side preloads (e.g., when hovering over a link).
- Use
ensureQueryDatato make sure the data is loaded before the component renders. - In your component, you can then use
useSuspenseQueryoruseQueryto access the prefetched data reactively.
If you only need non-reactive data (i.e., you don’t want live updates), you can use fetchQuery in the loader and pass the result to your component as loader data.
Note: Reactivity (live updates) is only achieved in the component using useQuery or useSuspenseQuery, not in the loader. The loader is for preloading or SSR, not for subscriptions or live updates. For more details and examples, see the Convex TanStack Start documentation and the Convex with TanStack Query docs.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@gusty fiber Kapa.ai is still learning and improving, please let me know how I did by reacting below.
how do I get access to queryClient
@tired lark so the loader is not meant for getting my reactive data?
Let me check the knowledge base for you again. One moment! :books:
Correct—the loader in TanStack Router is not meant for getting reactive (live-updating) data from Convex. Loaders are used for preloading or fetching data during SSR or before navigation, but they only run once per navigation or SSR pass. If you want reactivity (live updates as your data changes), you need to use Convex's useQuery or useSuspenseQuery hooks inside your React components. These hooks establish a live subscription to your Convex data, which loaders do not provide.
As explained by Convex support:
"Anything in a loader runs during SSR and during a preload (by default, hover over the link to the page). So start loading in the loader if you want to get a head start on loading a query, or block with
await ensureQueryin the loader if you never want your route to load without the query already being loaded.""When reactivity is needed is it only achieved in the route component?
Yep, you don't get reactivity unless you useuseQuery()."
Discord support thread: When to query data in a framework's loader vs a route component? Or imported component?
So, use the loader for preloading or SSR, and use useQuery/useSuspenseQuery in your components for live, reactive data.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@gusty fiber Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@tired lark how can I get access to queryClient from context?
Let me check the knowledge base for you again. One moment! :books:
You can access the queryClient from context in your loader or component by using the context object provided by TanStack Router. When you set up your router, you define the context (including queryClient), and then it is available in your route loaders and components.
For example, when creating your router, you might do:
import { QueryClient } from "@tanstack/react-query";
import { routerWithQueryClient } from "@tanstack/react-router-with-query";
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";
export function createRouter() {
const queryClient = new QueryClient();
const router = routerWithQueryClient(
createTanStackRouter({
routeTree,
context: { queryClient },
// ...
}),
queryClient
);
return router;
}
Then, in your route definition, you can access queryClient from the opts.context parameter in the loader:
export const Route = createFileRoute("/posts")({
loader: async (opts) => {
// Access queryClient from context
await opts.context.queryClient.ensureQueryData(
convexQuery(api.messages.list, {})
);
},
// ...
});
Server-side Rendering with TanStack Start
So, queryClient is made available via the context you define when setting up your router, and you access it in loaders or components through the opts.context parameter.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@gusty fiber Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@tired lark this is how my main tsx looks
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import { ConvexProvider, ConvexReactClient } from 'convex/react';
import { ConvexQueryClient } from '@convex-dev/react-query';
import { QueryClient } from '@tanstack/react-query';
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
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);
// Create a new router instance
const router = createRouter({ routeTree, defaultPreload: "intent", context: { queryClient } })
// Register the router instance for type safety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ConvexProvider client={convex}>
<RouterProvider router={router} />
</ConvexProvider>
</StrictMode>
)
Let me check the knowledge base for you again. One moment! :books:
Your setup is almost correct, but to ensure that queryClient is available in your route loaders and components via context, you need to make sure that:
- You pass the
context(withqueryClient) when creating the router. - In your route definitions (e.g., in loaders), you access it via
opts.context.queryClient.
Your code does this:
const router = createRouter({
routeTree,
defaultPreload: "intent",
context: { queryClient }
});
This means in your route files, you can access queryClient like this:
export const Route = createFileRoute("/some-path")({
loader: async (opts) => {
// Access queryClient from context
await opts.context.queryClient.ensureQueryData(
convexQuery(api.someQuery, {})
);
},
// ...
});
This pattern is confirmed in the TanStack Start Quickstart and SSR documentation.
Summary:
You are correctly providing queryClient in the router context, and you can access it in your loaders via opts.context.queryClient. No further changes are needed for this part of your setup.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@gusty fiber Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@tired lark I keep getting this error "Property 'queryClient' does not exist on type '{}'."
Let me check the knowledge base for you again. One moment! :books:
The error "Property 'queryClient' does not exist on type '{}'" usually means that the context type for your router is not properly set up or inferred, so TypeScript thinks the context is just {} (an empty object).
How to fix it:
You need to explicitly declare the context type when creating your root route. In TanStack Router, this is done using createRootRouteWithContext (or createFileRouteWithContext for file-based routes), where you specify the shape of your context object.
For example, in your __root.tsx or root route file, do:
import { QueryClient } from "@tanstack/react-query";
import { createRootRouteWithContext } from "@tanstack/react-router";
export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
}>()({
// ... your route config
});
This tells TypeScript that your context will have a queryClient property, so in your loaders you can safely access opts.context.queryClient without type errors.
This pattern is shown in the TanStack Start Quickstart:
export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
}>()({
// ...
});
Summary:
Add the correct context type to your root route using createRootRouteWithContext<{ queryClient: QueryClient }>(). This will resolve the TypeScript error and allow you to access queryClient from context in your loaders and components.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@gusty fiber Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@tired lark is there a benifit to using TanStack query with Tanstack router using convex
Let me check the knowledge base for you again. One moment! :books:
Yes, there are benefits to using TanStack Query with TanStack Router when working with Convex:
-
Live-updating queries: TanStack Query, when used with Convex, allows you to subscribe to live-updating data. This means your UI will automatically update in real-time as your Convex backend data changes, without manual polling or refetching. This is achieved by using hooks like
useQueryoruseSuspenseQuerywith theconvexQueryhelper, which set up reactive subscriptions to your Convex queries Client > Convex with TanStack Query. -
Loader-based preloading and SSR: TanStack Router's loaders can be used to preload data for routes, improving perceived performance by ensuring data is available as soon as a route is rendered. While loaders themselves are not reactive, they work well with TanStack Query's caching and prefetching, and you can use
useQueryin your components for reactivity Client > React > TanStack Start. -
Consistent query state and caching: TanStack Query manages query caching, deduplication, and background updates. When combined with Convex, this means you get both Convex's real-time updates and TanStack Query's cache management, which can help with performance and user experience Client > Convex with TanStack Query.
-
Seamless integration: The Convex TanStack Query adapter is designed to work smoothly with TanStack Router, making it easy to set up a consistent, type-safe, and reactive data layer in your app Client > React > TanStack Start.