Skip to content

atmosphere.js

TypeScript client for the Atmosphere Framework. Supports WebSocket, SSE, and Long-Polling transports with first-class React, Vue, and Svelte hooks.

Terminal window
npm install atmosphere.js
import { atmosphere } from 'atmosphere.js';
const subscription = await atmosphere.subscribe({
url: 'http://localhost:8080/chat',
transport: 'websocket',
}, {
message: (response) => console.log('Received:', response.responseBody),
open: (response) => console.log('Connected via:', response.transport),
close: (response) => console.log('Connection closed'),
error: (error) => console.error('Error:', error),
});
subscription.push({ user: 'John', message: 'Hello World' });

All hooks require an <AtmosphereProvider> ancestor.

import { AtmosphereProvider } from 'atmosphere.js/react';
function App() {
return (
<AtmosphereProvider>
<Chat />
</AtmosphereProvider>
);
}

Subscribe to an endpoint:

import { useAtmosphere } from 'atmosphere.js/react';
function Chat() {
const { data, state, push } = useAtmosphere<Message>({
request: { url: '/chat', transport: 'websocket' },
});
return state === 'connected'
? <button onClick={() => push({ text: 'Hello' })}>Send</button>
: <p>Connecting...</p>;
}

Join a room with presence:

import { useRoom } from 'atmosphere.js/react';
function ChatRoom() {
const { joined, members, messages, broadcast } = useRoom<ChatMessage>({
request: { url: '/atmosphere/room', transport: 'websocket' },
room: 'lobby',
member: { id: 'user-1' },
});
return (
<div>
<p>{members.length} online</p>
{messages.map((m, i) => <div key={i}>{m.member.id}: {m.data.text}</div>)}
<button onClick={() => broadcast({ text: 'Hi' })}>Send</button>
</div>
);
}

Lightweight presence tracking:

import { usePresence } from 'atmosphere.js/react';
function OnlineUsers() {
const { members, count, isOnline } = usePresence({
request: { url: '/atmosphere/room', transport: 'websocket' },
room: 'lobby',
member: { id: currentUser.id },
});
return <p>{count} users online. Alice is {isOnline('alice') ? 'here' : 'away'}.</p>;
}

AI/LLM text streaming:

import { useStreaming } from 'atmosphere.js/react';
function AiChat() {
const { fullText, isStreaming, stats, routing, send } = useStreaming({
request: { url: '/ai/chat', transport: 'websocket' },
});
return (
<div>
<button onClick={() => send('Explain WebSockets')} disabled={isStreaming}>Ask</button>
<p>{fullText}</p>
{stats && <small>{stats.totalStreamingTexts} streaming texts</small>}
</div>
);
}

Vue composables do not require a provider — they create or accept an Atmosphere instance directly.

<script setup lang="ts">
import { useAtmosphere } from 'atmosphere.js/vue';
const { data, state, push } = useAtmosphere<ChatMessage>({
url: '/chat',
transport: 'websocket',
});
</script>
<template>
<p>Status: {{ state }}</p>
<button @click="push({ text: 'Hello!' })">Send</button>
</template>
<script setup lang="ts">
import { useRoom } from 'atmosphere.js/vue';
const { members, messages, broadcast } = useRoom<ChatMessage>(
{ url: '/atmosphere/room', transport: 'websocket' },
'lobby',
{ id: 'user-1' },
);
</script>
<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>
<script setup lang="ts">
import { useStreaming } from 'atmosphere.js/vue';
const { fullText, isStreaming, send, reset } = useStreaming({
url: '/ai/chat',
transport: 'websocket',
});
</script>
<template>
<button @click="send('What is Atmosphere?')">Ask</button>
<p>{{ fullText }}</p>
<span v-if="isStreaming">Generating...</span>
</template>

Svelte integrations use the store pattern — each factory returns a Svelte-compatible readable store plus action functions.

<script>
import { createAtmosphereStore } from 'atmosphere.js/svelte';
const { store: chat, push } = createAtmosphereStore({ url: '/chat', transport: 'websocket' });
</script>
<p>Status: {$chat.state}</p>
<button on:click={() => push({ text: 'Hello!' })}>Send</button>
<script>
import { createRoomStore } from 'atmosphere.js/svelte';
const { store: lobby, broadcast } = createRoomStore(
{ url: '/atmosphere/room', transport: 'websocket' },
'lobby',
{ id: 'user-1' },
);
</script>
<p>Members: {$lobby.members.map(m => m.id).join(', ')}</p>
<button on:click={() => broadcast({ text: 'Hello!' })}>Broadcast</button>
<script>
import { createPresenceStore } from 'atmosphere.js/svelte';
const presence = createPresenceStore(
{ url: '/atmosphere/room', transport: 'websocket' },
'lobby',
{ id: 'user-1' },
);
</script>
<p>{$presence.count} users online</p>
<script>
import { createStreamingStore } from 'atmosphere.js/svelte';
const { store, send, reset } = createStreamingStore({
url: '/ai/chat',
transport: 'websocket',
});
</script>
<button on:click={() => send('What is Atmosphere?')}>Ask</button>
<p>{$store.fullText}</p>
{#if $store.isStreaming}<span>Generating...</span>{/if}

The server sends JSON messages using the Atmosphere AI streaming protocol:

{"type": "streaming-text", "data": "Hello", "sessionId": "abc-123", "seq": 1}
{"type": "progress", "data": "Thinking...", "sessionId": "abc-123", "seq": 2}
{"type": "metadata", "key": "model", "value": "gpt-4", "sessionId": "abc-123", "seq": 3}
{"type": "complete", "data": "Done", "sessionId": "abc-123", "seq": 10}
{"type": "error", "data": "Rate limited","sessionId": "abc-123", "seq": 11}

Use subscribeStreaming for framework-agnostic streaming, or the hooks above for React/Vue/Svelte.

OptionTypeDescription
urlstringEndpoint URL
transportstring'websocket', 'sse', 'long-polling', 'streaming'
fallbackTransportstringTransport to use if primary fails
reconnectbooleanEnable auto-reconnection
reconnectIntervalnumberTime between reconnections (ms)
maxReconnectOnClosenumberMaximum reconnection attempts
trackMessageLengthbooleanEnable message length tracking
headersobjectCustom headers
withCredentialsbooleanInclude credentials
  • Chrome/Edge: last 2 versions
  • Firefox: last 2 versions + ESR
  • Safari: last 2 versions
  • Mobile Safari (iOS): last 2 versions
  • Chrome Android: last 2 versions