Activity Tracking
This page documents the loading tracker hooks for Apollo operations:
useApolloTracker— global state: counters per owner and aggregated globally.useApolloTracking— bind aRef<boolean>to the tracker automatically.- Owner-scoped helpers (same usage pattern):
useMutationsLoading(id?)useQueriesLoading(id?)useSubscriptionsLoading(id?)
All hooks share the same goal: provide consistent, reactive visibility into what’s loading in your app.
When to use
- Show a global loading bar/overlay when anything is in-flight.
- Disable a section/component while its own requests run.
- Debug which component is performing queries/mutations/subscriptions.
Quick start
Track a loading ref automatically
ts
import { ref } from 'vue'
import { useApolloTracking } from '@vue3-apollo/core'
const loading = ref(false)
useApolloTracking({
state: loading,
type: 'queries',
})
// later
loading.value = true
// ... do work
loading.value = falseCheck if current component is busy (representative for all owner-scoped hooks)
ts
import { useQueriesLoading } from '@vue3-apollo/core'
const isBusy = useQueriesLoading() // also: useMutationsLoading(), useSubscriptionsLoading()API
| Hook | Purpose | Signature |
|---|---|---|
useApolloTracker | Global counters + per-owner map; manual track() | const { activeGlobal, activeByOwner, track } = useApolloTracker() |
useApolloTracking | Auto-forward a Ref<boolean> into counters | useApolloTracking({ state, type }) |
useMutationsLoading | Is any mutation active for owner? | useMutationsLoading(id?) => ComputedRef<boolean> |
useQueriesLoading | Is any query active for owner? | useQueriesLoading(id?) => ComputedRef<boolean> |
useSubscriptionsLoading | Is any subscription active for owner? | useSubscriptionsLoading(id?) => ComputedRef<boolean> |
Types
- Operation types:
'queries' | 'mutations' | 'subscriptions' - Owner id:
number | string(componentuid, feature key, etc.)
Core concepts
- Global vs owner counters:
activeGlobalaggregates across the app;activeByOwnertracks perid. - Owner resolution: Owner-scoped hooks use the current component
uidunless you pass a customid. - Reactivity: counters update via shallow refs; spreads are used internally to trigger Vue reactivity.
- Client-only:
useApolloTrackingis a no-op on the server and also cleans up on scope dispose.
Examples
1) Global indicator (using tracker directly)
ts
import { computed } from 'vue'
import { useApolloTracker } from 'vue3-apollo'
const { activeGlobal } = useApolloTracker()
const isAnyQueryLoading = computed(() => (activeGlobal.value.queries ?? 0) > 0)vue
<template>
<LoadingBar v-if="isAnyQueryLoading" />
</template>2) Component-level guard (owner-scoped, representative for all three)
vue
<script setup lang="ts">
import { useQueriesLoading } from '@vue3-apollo/core'
// Tracks current component by default; pass an id to share state across pieces
const isLoading = useQueriesLoading()
</script>
<template>
<button :disabled="isLoading">
Refresh Data
</button>
</template>3) Composable pattern with auto-tracking
ts
import { ref } from 'vue'
import { useApolloTracking } from 'vue3-apollo'
export function useSaveItem() {
const saving = ref(false)
useApolloTracking({
state: saving,
type: 'mutations',
})
const run = async (input: any) => {
try {
saving.value = true
// await apolloClient.mutate(...)
}
finally {
saving.value = false
}
}
return {
run,
saving,
}
}Patterns & tips
- Choose one source of truth: Prefer
useApolloTrackingin composables that manage aloadingref. - Stable owner ids: For shared sections, pass a custom
id(e.g.,'dashboard') to owner-scoped hooks. - Never let counters go negative: Internals clamp at
0; ensure eachtruehas a matchingfalse. - SSR: Tracking only happens on the client; it won’t interfere with SSR.
Errors & edge cases
- Using owner-scoped hooks outside a component without an explicit
idreturnsfalse(nouid). - If you unmount mid-load,
useApolloTrackingperforms cleanup on dispose to avoid counter leaks.