Subscriptions ​
This page shows how to use GraphQL subscriptions with the useSubscription composable for real-time updates.
Transport Setup ​
Unlike queries and mutations, subscriptions require a persistent connection to your GraphQL server. The standard HttpLink does not support subscriptions—you need to configure either SSE or WebSocket transport.
See The Guild's comparison to help decide which transport is right for your use case.
Overview ​
Like queries, subscriptions fetch data. Unlike queries, subscriptions maintain an active connection to your GraphQL server, enabling the server to push updates to your client in real time.
Subscriptions are useful for:
- Small, incremental changes to large objects - Fetch initial state with a query, then receive updates to individual fields as they occur
- Low-latency, real-time updates - Chat messages, notifications, live data feeds
When to use subscriptions
For most use cases, prefer polling or refetching on demand. Use subscriptions only when you need real-time push updates from the server.
Executing a Subscription ​
Use useSubscription to subscribe to real-time data:
<script setup lang="ts">
const { result, loading, error } = useSubscription(gql`
subscription OnNewMessage($channelId: ID!) {
newMessage(channelId: $channelId) {
id
text
author
}
}
`, {
variables: { channelId: '1' },
})
</script>
<template>
<div v-if="loading">
Connecting...
</div>
<div v-else-if="error">
Error: {{ error.message }}
</div>
<div v-else-if="result">
New message from {{ result.newMessage.author }}: {{ result.newMessage.text }}
</div>
</template>The useSubscription composable returns:
result- A ref containing the latest subscription dataloading- A ref that istrueuntil the first event is receivederror- A ref containing any error that occurredvariables- A ref containing the current variables
Variables ​
Like queries, subscriptions support multiple levels of reactivity. Pass variables in the options object:
const channelId = ref('general')
const { result } = useSubscription(ON_NEW_MESSAGE, {
variables: {
channelId, // Reactive - subscription restarts when value changes
},
})Or use a getter function:
const { channelId } = defineProps<{ channelId: string }>()
const { result } = useSubscription(ON_NEW_MESSAGE, () => ({
variables: { channelId },
}))Lifecycle Control ​
Control the subscription with start, stop, and restart:
<script setup lang="ts">
const { result, start, stop, restart } = useSubscription(NOTIFICATIONS)
function reconnect() {
restart() // Disconnect and reconnect
}
</script>
<template>
<div>
<button @click="stop">
Pause
</button>
<button @click="start">
Resume
</button>
<button @click="reconnect">
Reconnect
</button>
</div>
</template>Disabling the Subscription ​
Use the enabled option to conditionally enable the subscription:
const isConnected = ref(true)
const { result } = useSubscription(NOTIFICATIONS, {
enabled: isConnected, // Subscription only active when true
})Event Hooks ​
React to subscription lifecycle events:
const { onResult, onError, onComplete } = useSubscription(ON_NEW_MESSAGE, {
variables: { channelId: '1' },
})
onResult((result) => {
console.log('New message:', result.newMessage)
})
onError((error) => {
console.error('Subscription error:', error)
})
onComplete(() => {
console.log('Subscription completed')
})Subscribing to Query Updates ​
Use subscribeToMore from useQuery to update query results with subscription data. This is useful when you want to fetch initial data with a query and then receive real-time updates.
<script setup lang="ts">
const { channelId } = defineProps<{ channelId: string }>()
const { current, subscribeToMore } = useQuery(GET_MESSAGES, {
variables: { channelId: () => channelId },
})
// Subscribe to new messages once query loads
watch(
() => current.value.resultState === 'complete',
(isComplete) => {
if (!isComplete)
return
subscribeToMore({
document: ON_NEW_MESSAGE,
variables: { channelId },
// First document is deprecated, use `previousData` with `complete` flag
updateQuery(_prev, { subscriptionData, previousData, complete }) {
if (!complete)
return
if (!subscriptionData.data)
return previousData
return {
...previousData,
messages: [
...previousData.messages,
subscriptionData.data.newMessage,
],
}
},
})
},
{ immediate: true },
)
</script>
<template>
<div v-if="current.loading">
Loading...
</div>
<div v-else-if="current.error">
Error: {{ current.error.message }}
</div>
<ul v-else-if="current.resultState === 'complete'">
<li v-for="msg in current.result.messages" :key="msg.id">
{{ msg.text }}
</li>
</ul>
</template>See SubscribeToMoreOptions for all available options.
Options ​
See useSubscription.Options for all available options.