Connection State
Present offline, reconnecting, and pending-operation state without confusing it with backend health.
useConvexConnectionState() observes the current primary Convex client's connection state.
const { state, isConnected, isReconnecting, pendingMutations, pendingActions } =
useConvexConnectionState()Offline banner
<script setup lang="ts">
const { isConnected, isReconnecting } = useConvexConnectionState()
// This delay is a product choice, so the application owns it.
const showOfflineBanner = ref(false)
let timer: ReturnType<typeof setTimeout> | undefined
let stopWatching: (() => void) | undefined
onMounted(() => {
stopWatching = watch(
isConnected,
(connected) => {
clearTimeout(timer)
if (connected) {
showOfflineBanner.value = false
return
}
timer = setTimeout(() => {
showOfflineBanner.value = true
}, 500)
},
{ immediate: true },
)
})
onBeforeUnmount(() => {
stopWatching?.()
clearTimeout(timer)
})
</script>
<template>
<aside v-if="showOfflineBanner" role="status">
{{ isReconnecting ? 'Reconnecting to live updates…' : 'Live updates are offline.' }}
</aside>
</template>The composable reports transport facts immediately. The example delays its banner until after mount so SSR stays deterministic and the application—not the library—owns the presentation policy.
What connection means
isConnected reports the WebSocket connection. It does not prove:
- every backend dependency is healthy;
- a pending mutation committed;
- the browser has general internet access;
- a third-party API called by an action is available.
Use operation results and product queries for those facts.
Pending operations
pendingMutations and pendingActions come from the Convex connection state. They support a general “saving” indicator, not per-form attribution.
Identity changes
When the runtime replaces the primary client, connection state resets to the disconnected snapshot and observation rebinds to the replacement. Do not preserve the old client's connected badge through the transition.
Cleanup
Mounted consumers share one underlying connection-state subscription. The runtime subscribes for the first consumer and removes it after the last scope ends.
Calling the composable outside a component or Vue effect scope throws immediately. A connection subscription needs an explicit lifecycle owner; utility code should accept the connection state from its caller or run inside an owned effect scope.