useRealtimePresence
useRealtimePresence
Track which clients are currently present in a room, with optional info about each one (a name, avatar, cursor position). Useful for "who's viewing this page" indicators, avatar stacks, and online lists.
Usage
<script setup lang="ts">
const { members, join, leave } = useRealtimePresence('document:123', {
info: { name: 'Alice', avatarUrl: '/alice.png' },
})
</script>
<template>
<UAvatarGroup>
<UAvatar
v-for="(info, connectionId) in members"
:key="connectionId"
:src="info.avatarUrl"
:text="info.name[0]"
/>
</UAvatarGroup>
</template>
Type Signature
function useRealtimePresence<TInfo = unknown>(
room: string,
options?: UseRealtimePresenceOptions<TInfo>
): UseRealtimePresenceReturn<TInfo>
Parameters
room
- Type:
string - Required: Yes
An opaque group tag. All clients calling useRealtimePresence with the same room string see each other. It's just a string key, not tied to routes or URLs, so you can scope it however makes sense (a document id, a page path, a chat channel).
options
- Type:
UseRealtimePresenceOptions<TInfo> - Required: No
interface UseRealtimePresenceOptions<TInfo> {
/**
* Opaque data shown to other room members (e.g. `{ name, avatarUrl }`). Given up front, this
* client auto-joins the room on setup; omit it to only observe who else is present.
*/
info?: TInfo
}
Passing info joins the room immediately as a member. Omitting it lets you observe who's present without appearing in members yourself, useful for a spectator view.
Return Value
interface UseRealtimePresenceReturn<TInfo> {
members: Readonly<Ref<Record<string, TInfo>>>
join: () => Promise<void>
leave: () => Promise<void>
}
members
A readonly ref keyed by connection id, with the info each member joined with:
const { members } = useRealtimePresence('document:123')
watchEffect(() => {
console.log(`${Object.keys(members.value).length} people here`)
})
join()
Joins the room using the info passed to the composable. Called automatically if info was provided; call it manually if you joined without info and want to become a member later:
const { join } = useRealtimePresence('document:123')
function startEditing() {
join()
}
leave()
Leaves the room. Also called automatically on unmount:
const { leave } = useRealtimePresence('document:123')
function stopEditing() {
leave()
}
Examples
Who's Viewing This Page
Show an avatar stack of everyone currently viewing:
<script setup lang="ts">
const props = defineProps<{ documentId: string }>()
const currentUser = useCurrentUser()
const { members } = useRealtimePresence(`document:${props.documentId}`, {
info: { name: currentUser.name, avatarUrl: currentUser.avatarUrl },
})
</script>
<template>
<UAvatarGroup :max="5">
<UAvatar
v-for="(info, connectionId) in members"
:key="connectionId"
:src="info.avatarUrl"
:alt="info.name"
:text="info.name[0]"
/>
</UAvatarGroup>
</template>
Read-Only Viewer Count
Observe the room without joining it, useful for a dashboard that shows activity without appearing as a "viewer" itself:
<script setup lang="ts">
const props = defineProps<{ documentId: string }>()
// No `info` passed: this client observes but doesn't join
const { members } = useRealtimePresence(`document:${props.documentId}`)
</script>
<template>
<UBadge color="neutral" variant="subtle" icon="i-lucide-eye">
{{ Object.keys(members).length }} {{ Object.keys(members).length === 1 ? 'person' : 'people' }} currently viewing
</UBadge>
</template>