#Accessing the convex client from outside React

6 messages · Page 1 of 1 (latest)

tribal quartz
#

Is there a client api to access the current state of a convex query? I need to access the query state from a global store like Zustand but can’t find a way to do so.

my convex client is created in my tanstack start getRouter

export function getRouter() {
const CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL!;
if (!CONVEX_URL) {
console.error("missing envar VITE_CONVEX_URL");
}
const convexQueryClient = new ConvexQueryClient(CONVEX_URL);

const queryClient: QueryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});
convexQueryClient.connect(queryClient);

const router = routerWithQueryClient(
createRouter({
routeTree,
defaultPreload: "intent",
context: { queryClient },
scrollRestoration: true,
Wrap: ({ children }) => (
<ConvexProvider client={convexQueryClient.convexClient}>
{children}
</ConvexProvider>
),
}),
queryClient,
);

return router;
}

can I still globally access convexQueryClient.convexClient or the convexClient?

mossy latchBOT
#

Hi @tribal quartz. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

You can access Convex from outside React, but there are a couple of nuances with your current setup.

1. Convex clients can be used outside React

Convex has two main JS clients:

  • ConvexClient / ConvexHttpClient from convex/browser – usable in any JS environment (Node, browser, outside React). [JS clients]
  • ConvexReactClient from convex/react – designed for React, normally used via ConvexProvider + hooks. [React module]

The TanStack adapter ConvexQueryClient is just a bridge between Convex and TanStack Query; it exposes a convexClient you pass into <ConvexProvider>. [TanStack Query setup]

So yes, you can call methods on that underlying client from non-React code, as long as you have a reference to it.

2. Getting the current state of a query

Convex itself doesn’t expose a “global cache” API like TanStack Query does. The closest low-level primitive is the Watch<T> interface, which has:

  • onUpdate(callback) – subscribe to changes
  • localQueryResult() – get the current result if already subscribed [Watch interface]

However, that Watch API is surfaced via the React client and hooks; the docs don’t show a public, non-React way to grab an arbitrary query’s current value from a global cache. The knowledge sources do not document a supported “read any query’s current value by key” API on ConvexReactClient or ConvexClient.

Because of that, the recommended pattern in the docs/Discord is:

#
  • Keep the subscription “higher up” / outside the component that needs it, and
  • Expose the data via your own global store (Zustand, Valtio, etc.), while the Convex subscription keeps running. [Discord pagination thread]

Example pattern (shown with Valtio in the Discord thread, but the same idea applies to Zustand):

  1. Create a provider that runs the Convex query (or useConvexPaginatedQuery / TanStack useQuery(convexQuery(...))).
  2. Mirror that result into a global store.
  3. Consume from the store anywhere (including non-React code, if your store supports it).

The docs explicitly suggest “raising that state up somewhere where it will persist” to keep subscriptions alive and share them. [Discord pagination thread]

3. In your TanStack Start setup

In your getRouter, you already create:

const convexQueryClient = new ConvexQueryClient(CONVEX_URL);
// ...
<ConvexProvider client={convexQueryClient.convexClient}>
  {children}
</ConvexProvider>

The docs’ TanStack Start quickstart uses the same pattern and only passes queryClient into the router context. [TanStack Start quickstart]

Another guide shows adding both convexClient and convexQueryClient to the router context so they’re globally accessible: [Better-auth TanStack guide]

#
const router = routerWithQueryClient(
  createRouter({
    routeTree,
    defaultPreload: 'intent',
    scrollRestoration: true,
    context: {
      queryClient,
      convexClient: convexQueryClient.convexClient,
      convexQueryClient,
    },
    Wrap: ({ children }) => (
      <ConvexProvider client={convexQueryClient.convexClient}>
        {children}
      </ConvexProvider>
    ),
  }),
  queryClient,
)

That pattern gives you a global reference to the Convex client(s). From there you can:

  • Use convexClient directly in non-React code (e.g. to run queries/mutations via the JS client API). [JS clients]
  • Or wire TanStack Query’s cache into your Zustand store (since TanStack Query does have APIs to read current query data), using queryClient that you already have.

4. Directly reading Convex query state

The knowledge sources do not document a supported API like:

convexQueryClient.getCurrentResult(api.myQuery, args)

or similar. To get “current state” for use in a global store, the documented options are:

  • Use TanStack Query’s queryClient.getQueryData(...) (since you’re already using @convex-dev/react-query and useQuery(convexQuery(...))), and sync that into Zustand.
  • Or keep a Convex subscription (via React hooks or ConvexClient) in a long-lived provider and mirror its result into your store, as in the Valtio example from Discord. [TanStack Query integration; Discord pagination thread]

So:

#
  • Yes, you can globally access convexQueryClient and convexQueryClient.convexClient by putting them into your router context (or another module-level singleton).
  • No, the docs do not show a built-in “global query cache read” API on the Convex client itself; to get current query state for a global store, use TanStack Query’s cache or your own subscription + store pattern.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: