Composables

useRealtimeEvents

Publish and subscribe to events across clients.

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:

ParameterTypeDescription
channelstringThe channel name to subscribe to
callback(data: T) => voidFunction called when an event is received
optionsUseRealtimeEventsSubscribeOptionsOptional 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:

ParameterTypeDescription
channelstringThe channel name to publish to
dataTThe event data to send
optionsUseRealtimeEventsSubscribeOptionsOptional 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:

Chat.vue
<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:

Notifications.vue
<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:

LiveCursors.vue
<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:

TypingIndicator.vue
<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

FeatureuseRealtimeStateuseRealtimeEvents
PersistenceState persisted on serverEvents are ephemeral
Initial ValueLoaded from server on mountNo initial value
Use CaseShared data (counters, lists)Notifications, messages
API StyleReactive refPub/sub callbacks

Use useRealtimeState when you need persistent, shared state. Use useRealtimeEvents when you want to broadcast ephemeral messages or notifications.