> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/get-convex/convex-react-query/llms.txt
> Use this file to discover all available pages before exploring further.

# Differences from fetch-based queries

> Key behavioral differences between Convex reactive queries and standard fetch-based TanStack Query usage.

## `staleTime: Infinity`

This is the most significant behavioral difference. With regular fetch-based queries, TanStack Query marks data as stale after `staleTime` elapses and schedules a background refetch. With `convexQuery`, the Convex server pushes updates proactively over WebSocket — the client never needs to ask for fresh data.

`staleTime: Infinity` is set automatically by `convexQuery()` and `convexAction()`, so TanStack Query will never trigger a background refetch for these queries.

```tsx theme={null}
// fetch-based: data goes stale and triggers background refetches
useQuery({
  queryKey: ["messages"],
  queryFn: () => fetch("/api/messages").then(r => r.json()),
  staleTime: 30_000, // refetch after 30s
});

// Convex: data is never stale — pushed from the server
useQuery(convexQuery(api.messages.list, {}));
// staleTime: Infinity is set automatically
```

## No invalidation needed

With fetch-based mutations, you must call `queryClient.invalidateQueries()` after a successful mutation to prompt TanStack Query to refetch the affected data. With Convex, the backend pushes updated query results automatically after any mutation that changes the underlying data.

```tsx theme={null}
// fetch-based mutation: must manually invalidate
const { mutate } = useMutation({
  mutationFn: (body) => fetch("/api/messages", { method: "POST", body }),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ["messages"] }),
});

// Convex mutation: no invalidation needed
const { mutate } = useMutation({
  mutationFn: useConvexMutation(api.messages.send),
  // No onSuccess needed — updated results are pushed automatically
});
```

<Note>
  Calling `queryClient.invalidateQueries()` on a `convexQuery` is harmless but
  unnecessary. The cache will be updated by the Convex subscription before any
  refetch triggered by invalidation could complete.
</Note>

## `gcTime` controls subscription duration

With fetch-based queries, `gcTime` only determines how long an unused cache entry stays in memory before being garbage-collected. With Convex queries, `gcTime` also controls how long the **WebSocket subscription** stays active after all observers unmount.

While the subscription is alive (even with no mounted observers):

* The cached value continues to receive pushed updates from the server.
* Remounting a component that uses this query is instant — the fresh cached value is available immediately, with no loading state.

<Tip>
  Set `gcTime` to at least a few seconds for most queries. A value of `10_000`
  (10 seconds) is a reasonable default for components that mount and unmount
  regularly.
</Tip>

```tsx theme={null}
useQuery({
  ...convexQuery(api.messages.list, {}),
  gcTime: 10_000, // subscription stays active for 10s after last observer unmounts
});
```

Once `gcTime` expires, `ConvexQueryClient` calls `unsubscribe()` on the Convex `Watch` and the cache entry is removed.

## No effect from `refetchOnWindowFocus` / `refetchOnReconnect`

TanStack Query's `refetchOnWindowFocus` and `refetchOnReconnect` options trigger background fetches when the browser window regains focus or the network reconnects. These behaviors have no meaningful effect on reactive Convex queries because the Convex WebSocket connection handles reconnection and result freshness automatically.

You can safely leave these options at their defaults or disable them — either way they are harmless for `convexQuery`.

<Info>
  On reconnect, the Convex client re-establishes the WebSocket connection and
  re-runs all active subscriptions. Any changes that occurred while disconnected
  are pushed as part of the reconnect flow.
</Info>

## Actions vs. queries

`convexAction` does **not** receive pushed updates. Actions run imperatively and return a single result; they are not subscriptions.

Actions behave like standard fetch-based queries:

* They run once on mount.
* They refetch based on standard TanStack Query rules (`refetchOnWindowFocus`, `refetchOnReconnect`, `staleTime`).
* `invalidateQueries` can be used to trigger a manual refetch.

```tsx theme={null}
// Reactive — pushed updates from server
useQuery(convexQuery(api.messages.list, {}));

// Not reactive — fetches once, then follows standard refetch rules
useQuery(convexAction(api.weather.getSFWeather));
```

<Warning>
  `staleTime: Infinity` is also set on `convexAction` by default. If you want
  the action to refetch periodically, override `staleTime` explicitly:

  ```tsx theme={null}
  useQuery({
    ...convexAction(api.weather.getSFWeather),
    staleTime: 60_000, // refetch after 60s
  });
  ```
</Warning>

## Comparison table

|                            | `convexQuery`      | `convexAction`                       | fetch-based    |
| -------------------------- | ------------------ | ------------------------------------ | -------------- |
| Updates                    | Pushed from server | Poll / refetch                       | Poll / refetch |
| `staleTime`                | Always `Infinity`  | `Infinity` by default (configurable) | Configurable   |
| `invalidateQueries` needed | No                 | Sometimes                            | Yes            |
| SSR support                | Yes (HTTP)         | Yes (HTTP)                           | Yes            |
