-
-
Notifications
You must be signed in to change notification settings - Fork 761
Framework Hooks React Vue Svelte
atmosphere.js 5.0 ships first-class hooks for React, Vue, and Svelte that manage the full Atmosphere lifecycle — connection, reconnection, cleanup on unmount, and reactive state. No boilerplate WebSocket plumbing needed.
npm install atmosphere.js
The hooks are tree-shakeable sub-paths — only the framework you import gets bundled:
import { ... } from 'atmosphere.js/react'; // React 18+ import { ... } from 'atmosphere.js/vue'; // Vue 3 import { ... } from 'atmosphere.js/svelte'; // Svelte 4/5
All React hooks require an AtmosphereProvider ancestor that holds the shared Atmosphere client instance:
import { AtmosphereProvider } from 'atmosphere.js/react'; function App() { return ( <AtmosphereProvider config={{ logLevel: 'info' }}> <Chat /> </AtmosphereProvider> ); }
You can also pass a pre-built instance:
<AtmosphereProvider instance={myAtmosphere}>
Low-level hook for a raw Atmosphere subscription. Connects on mount, disconnects on unmount, re-connects when URL or transport changes.
import { useAtmosphere } from 'atmosphere.js/react'; function Notifications() { const { data, state, push, error } = useAtmosphere<Notification>({ request: { url: '/atmosphere/notifications', transport: 'websocket' }, }); if (state === 'connected' && data) { return <div>{data.text}</div>; } return <div>Connecting...</div>; }
Return type:
| Field | Type | Description |
|---|---|---|
subscription |
Subscription | null |
The active subscription (null before connected) |
state |
ConnectionState |
'disconnected' · 'connected' · 'reconnecting' · 'closed' · 'error'
|
data |
T | null |
Last received message (generic) |
error |
Error | null |
Last error |
push |
(msg) => void |
Send a message (string, object, or ArrayBuffer) |
Options:
| Field | Type | Default | Description |
|---|---|---|---|
request |
AtmosphereRequest |
(required) | Connection config (url, transport, etc.) |
enabled |
boolean |
true |
Set to false to defer connection |
Joins a room and tracks members, messages, and presence — reactive state updates on every event:
import { useRoom } from 'atmosphere.js/react'; interface ChatMessage { text: string; } function Chat() { const { joined, members, messages, broadcast, sendTo, error } = useRoom<ChatMessage>({ request: { url: '/atmosphere/room', transport: 'websocket' }, room: 'lobby', member: { id: 'alice', metadata: { name: 'Alice' } }, }); return ( <div> <p>{joined ? `Online: ${members.length}` : 'Joining...'}</p> <ul> {messages.map((m, i) => ( <li key={i}><b>{m.member.id}</b>: {m.data.text}</li> ))} </ul> <button onClick={() => broadcast({ text: 'Hello!' })}>Send</button> </div> ); }
Return type:
| Field | Type | Description |
|---|---|---|
joined |
boolean |
Whether the room has been joined |
members |
RoomMember[] |
Current room members |
messages |
Array<{ data: T; member: RoomMember }> |
Messages received (append-only) |
broadcast |
(data: T) => void |
Broadcast to all room members |
sendTo |
(memberId: string, data: T) => void |
Direct message to one member |
error |
Error | null |
Last error |
Convenience wrapper around useRoom that exposes only presence state — no messages:
import { usePresence } from 'atmosphere.js/react'; function OnlineIndicator() { const { members, count, isOnline } = usePresence({ request: { url: '/atmosphere/room', transport: 'websocket' }, room: 'lobby', member: { id: currentUser.id }, }); return ( <div> <span>{count} online</span> {isOnline('bob') && <span>Bob is here!</span>} </div> ); }
Return type:
| Field | Type | Description |
|---|---|---|
joined |
boolean |
Whether we have joined the room |
members |
RoomMember[] |
Current online members |
count |
number |
Number of members online |
isOnline |
(memberId: string) => boolean |
Check if a specific member is online |
Vue composables work without a provider — pass an Atmosphere instance or let each composable create one.
<script setup lang="ts"> import { useAtmosphere } from 'atmosphere.js/vue'; const { data, state, push } = useAtmosphere<ChatMessage>({ url: '/atmosphere/chat', transport: 'websocket', }); </script> <template> <div>State: {{ state }}</div> <div v-if="data">{{ data }}</div> <button @click="push(JSON.stringify({ text: 'Hello' }))">Send</button> </template>
Returns the same fields as the React hook, but as Vue Ref values (reactive).
<script setup lang="ts"> import { useRoom } from 'atmosphere.js/vue'; const { joined, members, messages, broadcast, sendTo } = useRoom<ChatMessage>( { url: '/atmosphere/room', transport: 'websocket' }, 'lobby', { id: 'alice' }, ); </script> <template> <div v-if="joined"> <p>{{ members.length }} members online</p> <div v-for="msg in messages" :key="msg.data.text"> <b>{{ msg.member.id }}</b>: {{ msg.data.text }} </div> <button @click="broadcast({ text: 'Hello!' })">Send</button> </div> <div v-else>Joining room...</div> </template>
Signature: useRoom<T>(request, roomName, member, instance?)
All returned values (joined, members, messages, error) are Vue Ref objects — they update reactively in templates and watchers.
<script setup lang="ts"> import { usePresence } from 'atmosphere.js/vue'; const { members, count, isOnline } = usePresence( { url: '/atmosphere/room', transport: 'websocket' }, 'lobby', { id: currentUser.id }, ); </script> <template> <span>{{ count }} online</span> </template>
count is a Vue computed ref — automatically recalculated when members change.
Svelte hooks use the Svelte store contract (subscribe method). Use $store auto-subscription syntax.
<script> import { createAtmosphereStore } from 'atmosphere.js/svelte'; const { store: chat, push } = createAtmosphereStore({ url: '/atmosphere/chat', transport: 'websocket', }); // $chat.state, $chat.data, $chat.error </script> <p>State: {$chat.state}</p> {#if $chat.data} <p>{JSON.stringify($chat.data)}</p> {/if} <button on:click={() => push(JSON.stringify({ text: 'Hello' }))}>Send</button>
Store state:
| Field | Type | Description |
|---|---|---|
state |
ConnectionState |
Connection state |
data |
T | null |
Last received message |
error |
Error | null |
Last error |
subscription |
Subscription | null |
Active subscription |
The store auto-connects when the first subscriber appears and disconnects when all subscribers are gone.
<script> import { createRoomStore } from 'atmosphere.js/svelte'; const { store: lobby, broadcast, sendTo } = createRoomStore( { url: '/atmosphere/room', transport: 'websocket' }, 'lobby', { id: 'alice' }, ); </script> {#if $lobby.joined} <p>{$lobby.members.length} members online</p> {#each $lobby.messages as msg} <p><b>{msg.member.id}</b>: {msg.data}</p> {/each} <button on:click={() => broadcast('Hello!')}>Send</button> {:else} <p>Joining room...</p> {/if}
Store state:
| Field | Type | Description |
|---|---|---|
joined |
boolean |
Whether the room has been joined |
members |
RoomMember[] |
Current room members |
messages |
Array<{ data: T; member: RoomMember }> |
Received messages |
error |
Error | null |
Last error |
<script> import { createPresenceStore } from 'atmosphere.js/svelte'; const presence = createPresenceStore( { url: '/atmosphere/room', transport: 'websocket' }, 'lobby', { id: currentUser.id }, ); </script> <span>{$presence.count} online</span> {#each $presence.members as m} <span>{m.id}</span> {/each}
Store state:
| Field | Type | Description |
|---|---|---|
joined |
boolean |
Whether we have joined the room |
members |
RoomMember[] |
Current online members |
count |
number |
Number of members online |
| Hook / Store | React | Vue | Svelte |
|---|---|---|---|
| Raw subscription | useAtmosphere |
useAtmosphere |
createAtmosphereStore |
| Room (messages + presence) | useRoom |
useRoom |
createRoomStore |
| Presence only | usePresence |
usePresence |
createPresenceStore |
| Provider / context | AtmosphereProvider |
(none needed) | (none needed) |
All hooks handle:
- ✅ Automatic connection on mount / first subscriber
- ✅ Automatic cleanup on unmount / last unsubscribe
- ✅ Reconnection (via Atmosphere's built-in reconnect)
- ✅ Full TypeScript generics for message types
interface RoomMember { id: string; metadata?: Record<string, unknown>; } interface PresenceEvent { type: 'join' | 'leave'; room: string; member: RoomMember; } type ConnectionState = 'disconnected' | 'connected' | 'reconnecting' | 'closed' | 'error';
- Getting Started with atmosphere.js 5.0 — core client library
- atmosphere.js API Reference — full type reference
- Understanding Rooms — server-side Room API
- Understanding @RoomService — declarative room handlers