useRealtimeEvents
useRealtimeEvents
Publish and subscribe to events across clients in real-time. Perfect for notifications, chat messages, live updates, or any ephemeral communication that doesn't need to be persisted.
Usage
<script setup>
const { subscribe, publish } = useRealtimeEvents()
// Subscribe to events
subscribe('notifications', (data) => {
console.log('Received:', data)
})
// Publish an event
function notify() {
publish('notifications', { message: 'Hello!' })
}
</script>
Type Signature
function useRealtimeEvents(
options?: UseRealtimeEventsOptions
): UseRealtimeEventsReturn
Options
interface UseRealtimeEventsOptions {
/**
* Timeout for publish acknowledgments in milliseconds
* @default 5000
*/
publishTimeout?: number
}
Return Value
interface UseRealtimeEventsReturn {
subscribe: <T>(
channel: string,
callback: (data: T) => void,
options?: UseRealtimeEventsSubscribeOptions
) => () => void
publish: <T>(
channel: string,
data: T,
options?: UseRealtimeEventsSubscribeOptions
) => Promise<void>
unsubscribe: (channel: string) => void
}
Methods
subscribe()
Subscribe to events on a channel.
subscribe<T>(
channel: string,
callback: (data: T) => void,
options?: UseRealtimeEventsSubscribeOptions
): () => void
Parameters:
| Parameter | Type | Description |
|---|---|---|
channel | string | The channel name to subscribe to |
callback | (data: T) => void | Function called when an event is received |
options | UseRealtimeEventsSubscribeOptions | Optional settings |
Returns: An unsubscribe function
const { subscribe } = useRealtimeEvents()
// Basic subscription
subscribe('chat', (message) => {
console.log('New message:', message)
})
// With TypeScript
interface ChatMessage {
user: string
text: string
timestamp: number
}
subscribe<ChatMessage>('chat', (message) => {
console.log(`${message.user}: ${message.text}`)
})
// Store unsubscribe function
const unsubscribe = subscribe('channel', callback)
// Later, unsubscribe
unsubscribe()
publish()
Publish an event to a channel.
publish<T>(
channel: string,
data: T,
options?: UseRealtimeEventsSubscribeOptions
): Promise<void>
Parameters:
| Parameter | Type | Description |
|---|---|---|
channel | string | The channel name to publish to |
data | T | The event data to send |
options | UseRealtimeEventsSubscribeOptions | Optional settings |
Returns: A Promise that resolves when the server acknowledges the event
const { publish } = useRealtimeEvents()
// Basic publish
await publish('notifications', { message: 'Hello!' })
// With error handling
try {
await publish('chat', {
user: 'Alice',
text: 'Hello everyone!',
timestamp: Date.now(),
})
} catch (error) {
console.error('Failed to send message:', error)
}
unsubscribe()
Unsubscribe from all callbacks on a channel.
unsubscribe(channel: string): void
const { subscribe, unsubscribe } = useRealtimeEvents()
subscribe('channel', callback1)
subscribe('channel', callback2)
// Remove all subscriptions for this channel
unsubscribe('channel')
Subscribe Options
interface UseRealtimeEventsSubscribeOptions {
/**
* Whether to receive events that this client published
* @default false
*/
includeSelf?: boolean
}
includeSelf
By default, when you publish an event, you don't receive it back. Set includeSelf: true to receive your own events:
const { subscribe, publish } = useRealtimeEvents()
// Won't receive own events (default)
subscribe('chat', (msg) => console.log(msg))
publish('chat', { text: 'Hello' }) // callback NOT triggered
// Will receive own events
subscribe('chat', (msg) => console.log(msg), { includeSelf: true })
publish('chat', { text: 'Hello' }) // callback IS triggered
// Can also set on publish
publish('chat', { text: 'Hello' }, { includeSelf: true })
Examples
Chat Messages
Real-time chat with message history:
<script setup>
const { subscribe, publish } = useRealtimeEvents()
const messages = ref([])
const input = ref('')
const userId = crypto.randomUUID()
interface Message {
id: string
userId: string
text: string
timestamp: number
}
subscribe<Message>('chat-messages', (message) => {
messages.value.push(message)
})
async function sendMessage() {
if (!input.value.trim()) return
await publish('chat-messages', {
id: crypto.randomUUID(),
userId,
text: input.value,
timestamp: Date.now(),
})
input.value = ''
}
</script>
<template>
<div>
<div class="messages">
<div
v-for="msg in messages"
:key="msg.id"
:class="msg.userId === userId ? 'own' : 'other'"
>
{{ msg.text }}
</div>
</div>
<form @submit.prevent="sendMessage">
<input v-model="input" placeholder="Type a message..." />
<button type="submit">Send</button>
</form>
</div>
</template>
Toast Notifications
Broadcast notifications to all clients:
<script setup>
const toast = useToast()
const { subscribe, publish } = useRealtimeEvents()
interface Notification {
title: string
description: string
color: 'success' | 'info' | 'warning' | 'error'
}
subscribe<Notification>('notifications', (notification) => {
toast.add({
title: notification.title,
description: notification.description,
color: notification.color,
})
})
function notifyAll(type: Notification['color']) {
publish('notifications', {
title: `${type.charAt(0).toUpperCase() + type.slice(1)} Alert`,
description: 'This notification was broadcast to all clients.',
color: type,
})
}
</script>
<template>
<div class="flex gap-2">
<button @click="notifyAll('success')">Success</button>
<button @click="notifyAll('info')">Info</button>
<button @click="notifyAll('warning')">Warning</button>
<button @click="notifyAll('error')">Error</button>
</div>
</template>
Live Cursors
Show other users' cursor positions:
<script setup>
const { subscribe, publish } = useRealtimeEvents()
const cursors = ref(new Map())
const myId = crypto.randomUUID()
interface CursorPosition {
userId: string
x: number
y: number
color: string
}
subscribe<CursorPosition>('cursors', (cursor) => {
if (cursor.userId !== myId) {
cursors.value.set(cursor.userId, cursor)
}
})
function handleMouseMove(event: MouseEvent) {
publish('cursors', {
userId: myId,
x: event.clientX,
y: event.clientY,
color: '#' + myId.slice(0, 6),
})
}
onMounted(() => {
window.addEventListener('mousemove', handleMouseMove)
})
onUnmounted(() => {
window.removeEventListener('mousemove', handleMouseMove)
})
</script>
<template>
<div>
<div
v-for="[id, cursor] in cursors"
:key="id"
class="cursor"
:style="{
left: cursor.x + 'px',
top: cursor.y + 'px',
backgroundColor: cursor.color,
}"
/>
</div>
</template>
Typing Indicators
Show when other users are typing:
<script setup>
const { subscribe, publish } = useRealtimeEvents()
const typingUsers = ref(new Set())
const myId = crypto.randomUUID()
let typingTimeout: ReturnType<typeof setTimeout>
subscribe<{ userId: string; isTyping: boolean }>('typing', ({ userId, isTyping }) => {
if (userId === myId) return
if (isTyping) {
typingUsers.value.add(userId)
} else {
typingUsers.value.delete(userId)
}
})
function onInput() {
publish('typing', { userId: myId, isTyping: true })
clearTimeout(typingTimeout)
typingTimeout = setTimeout(() => {
publish('typing', { userId: myId, isTyping: false })
}, 1000)
}
</script>
<template>
<div>
<input @input="onInput" placeholder="Type something..." />
<p v-if="typingUsers.size > 0">
{{ typingUsers.size }} user(s) typing...
</p>
</div>
</template>
Differences from useRealtimeState
| Feature | useRealtimeState | useRealtimeEvents |
|---|---|---|
| Persistence | State persisted on server | Events are ephemeral |
| Initial Value | Loaded from server on mount | No initial value |
| Use Case | Shared data (counters, lists) | Notifications, messages |
| API Style | Reactive ref | Pub/sub callbacks |
Use useRealtimeState when you need persistent, shared state. Use useRealtimeEvents when you want to broadcast ephemeral messages or notifications.