Package Exports
- @hardingjam/svelte-socket
Readme
svelte-socket
WebSocket wrapper for Svelte 5 using runes.
Overview
This library provides:
- SvelteSocket - WebSocket wrapper class with reactive state properties (
connectionStatus,sentMessages,receivedMessages) - SocketProvider - Context provider component that instantiates
SvelteSocketand makes it available to child components - Debugger - UI panel component showing connection status, sent messages, and received messages
- useSocket() - Hook to access socket instance from context
Features
- Constructor accepts configuration object with URL and optional callbacks (
onMessage,onOpen,onClose,onError) - Optional debug logging via
debugflag - Auto-reconnection with configurable delay and max attempts
- Reactive message history (sent and received)
- All state properties use Svelte 5
$staterunes - TypeScript support
Installation
npm install svelte-socketUsage
In parent components including +page.svelte or +layout.svelte...
<script>
import { SvelteSocket } from 'svelte-socket';
// Callbacks can be added on construction...
const socket = new SvelteSocket({
url: 'ws://localhost:8080',
onOpen: () => console.log('Hello, server!')
});
const isConnected = $derived(socket.connectionStatus === WebSocket.OPEN);
// ...or later using addEventListener
socket.addEventListener('message', (event) => {
console.log(event.data);
});
socket.sendMessage('text');
</script>
<SocketProvider {svelteSocket}>
{@render children()}
</SocketProvider>In any child component...
<script>
import { useSocket } from 'svelte-socket';
let { children } = $props();
const socket = useSocket()
let isConnected = $derived(socket.connectionStatus === WebSocket.OPEN);
let receivedMessages = $derived(socket.receivedMessages)
</script>
<p>{isConnected}</p>
{#each receivedMessages as msg, i}
<p>{msg.origin}<p>
{/each}
API
SvelteSocket
Constructor
new SvelteSocket(options: SocketConstructorArgs)Options:
| Property | Type | Required | Description |
|---|---|---|---|
url |
string |
Yes | WebSocket server URL |
onMessage |
(event: MessageEvent) => void |
No | Called when message received |
onOpen |
(event: Event) => void |
No | Called when connection opens |
onClose |
(event: CloseEvent) => void |
No | Called when connection closes |
onError |
(event: Event) => void |
No | Called on error |
debug |
boolean |
No | Enable console logging (default: false) |
reconnectOptions |
ReconnectOptions |
No | Auto-reconnection config |
maxMessageHistory |
number |
No | Max messages to keep in history (default: 50). Set to 0 for unlimited |
ReconnectOptions:
| Property | Type | Description |
|---|---|---|
enabled |
boolean |
Enable auto-reconnection |
delay |
number |
Milliseconds before reconnect attempt |
maxAttempts |
number |
Maximum reconnection attempts |
Example:
const socket = new SvelteSocket({
url: 'ws://localhost:8080',
debug: true,
maxMessageHistory: 100, // Keep last 100 messages (default: 50)
reconnectOptions: {
enabled: true,
delay: 1000,
maxAttempts: 5
},
onMessage: (event) => console.log(event.data),
onOpen: () => console.log('connected'),
onClose: () => console.log('disconnected')
});Properties
| Property | Type | Description |
|---|---|---|
connectionStatus |
WebSocket['readyState'] |
Connection state: 0 (CONNECTING), 1 (OPEN), 2 (CLOSING), 3 (CLOSED) |
sentMessages |
Array<{message: string | ArrayBuffer | Blob | ArrayBufferView, timestamp: number}> |
Sent message history (newest first, via unshift). Auto-trimmed to maxMessageHistory |
receivedMessages |
Array<{message: MessageEvent}> |
Received message history (newest first, via unshift). Auto-trimmed to maxMessageHistory |
maxMessageHistory |
number |
Maximum number of messages to keep in history (default: 50, readonly) |
Example:
<script>
const socket = new SvelteSocket({ url: 'ws://localhost:8080' });
const connectionStatus = $derived(socket.connectionStatus);
const sentMessages = $derived(socket.sentMessages);
const receivedMessages = $derived(socket.receivedMessages);
</script>
{#if connectionStatus === WebSocket.OPEN}
<p>Open</p>
{:else if connectionStatus === WebSocket.CONNECTING}
<p>Connecting</p>
{:else}
<p>Closed</p>
{/if}
<p>{sentMessages.length} sent</p>
<p>{receivedMessages.length} received</p>
{#each receivedMessages as { message }}
<div>{message.data}</div>
{/each}Methods
addEventListener(event, callback)
addEventListener(
event: 'message' | 'close' | 'open' | 'error',
callback: (event: MessageEvent | CloseEvent | Event) => void
): voidAdds event listener to WebSocket. The callback type is automatically inferred based on the event type:
'message'→(event: MessageEvent) => void'close'→(event: CloseEvent) => void'open'→(event: Event) => void'error'→(event: Event) => void
Throws if socket not connected.
socket.addEventListener('message', (event) => {
console.log(event.data); // event is typed as MessageEvent
});removeEventListener(event, callback)
removeEventListener(
event: 'message' | 'close' | 'open' | 'error',
callback: (event: MessageEvent | CloseEvent | Event) => void
): voidRemoves event listener. Callback types match addEventListener.
const handler = (event) => console.log(event.data);
socket.addEventListener('message', handler);
socket.removeEventListener('message', handler);sendMessage(message)
sendMessage(message: string | ArrayBuffer | Blob | ArrayBufferView): voidSends a text or binary message through the WebSocket. Supports strings, ArrayBuffer, Blob, TypedArray, and DataView. Stores in sentMessages array. Throws if not connected or not in OPEN state.
Example:
// Send text
socket.sendMessage('Hello, server!');
socket.sendMessage(JSON.stringify({ type: 'ping' }));
// Send binary data
const buffer = new Uint8Array([1, 2, 3, 4]);
socket.sendMessage(buffer);clearSentMessages()
clearSentMessages(): voidClears sentMessages array.
socket.clearSentMessages();clearReceivedMessages()
clearReceivedMessages(): voidClears receivedMessages array.
socket.clearReceivedMessages();removeSocket()
removeSocket(): voidCloses WebSocket connection. Clears message history. Prevents reconnection.
socket.removeSocket();SocketProvider
Context provider component that makes a socket instance available to child components.
Props:
| Property | Type | Required | Description |
|---|---|---|---|
url |
string |
Yes* | WebSocket server URL (automatically creates socket) |
svelteSocket |
SvelteSocket |
Yes* | Pre-configured SvelteSocket instance |
children |
Snippet |
No | Child components |
*At least one of url or svelteSocket must be provided. If both are provided, svelteSocket takes precedence.
Basic Usage:
The simplest way to use the provider is to pass a url, which automatically creates a SvelteSocket for you:
<script>
import { SocketProvider } from 'svelte-socket';
let { children } = $props();
</script>
<SocketProvider url="ws://localhost:8080">
{@render children()}
</SocketProvider>Custom Socket Configuration:
You can also create your own SvelteSocket with custom callbacks and options, then pass it to the provider:
<script>
import { SocketProvider, SvelteSocket } from 'svelte-socket';
let { children } = $props();
const svelteSocket = new SvelteSocket({
url: 'ws://localhost:8080',
debug: true,
reconnectOptions: {
enabled: true,
delay: 1000,
maxAttempts: 5
},
onMessage: (event) => {
console.log('Received:', event.data);
},
onOpen: () => {
console.log('Connected!');
}
});
</script>
<SocketProvider {svelteSocket}>
{@render children()}
</SocketProvider>useSocket()
useSocket(): SvelteSocketReturns socket instance from context.
Throws if not used within SocketProvider.
Example:
<script>
import { useSocket } from 'svelte-socket';
const socket = useSocket();
</script>Debugger
Debug UI component. Shows connection status, sent messages, received messages.
Props:
| Property | Type | Required |
|---|---|---|
socket |
SvelteSocket |
Yes |
Example:
<script>
import { SvelteSocket, Debugger } from 'svelte-socket';
const socket = new SvelteSocket({ url: 'ws://localhost:8080' });
</script>
<Debugger {socket} />Displays:
- Connection state (CONNECTING/OPEN/CLOSING/CLOSED)
- Message counts (sent/received)
- Sent message history with timestamps
- Received message history with data
Styling Requirements:
This component requires Tailwind CSS. You must configure Tailwind to scan the package files:
Tailwind v4:
/* app.css */
@import 'tailwindcss';
@source '../node_modules/@hardingjam/svelte-socket/dist/**/*.svelte';Tailwind v3:
// tailwind.config.js
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
'./node_modules/@hardingjam/svelte-socket/dist/**/*.svelte'
]
// ... rest of config
};Context Functions
setSocket(socket)
setSocket(socket: SvelteSocket): voidSets socket in Svelte context. Used by SocketProvider.
getSocketContext()
getSocketContext(): SvelteSocketGets socket from context. Throws if not found.
Examples
Basic
<script>
import { SvelteSocket } from 'svelte-socket';
const socket = new SvelteSocket({ url: 'ws://localhost:8080' });
socket.addEventListener('message', (event) => {
console.log(event.data);
});
function send() {
socket.sendMessage('test');
}
</script>
<button onclick={send}>Send</button>Auto-reconnect
<script>
import { SvelteSocket } from 'svelte-socket';
const socket = new SvelteSocket({
url: 'ws://localhost:8080',
debug: true,
reconnectOptions: {
enabled: true,
delay: 1000,
maxAttempts: 5
}
});
</script>JSON Messages
<script>
import { SvelteSocket } from 'svelte-socket';
const socket = new SvelteSocket({ url: 'ws://localhost:8080' });
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'ping') {
socket.sendMessage(JSON.stringify({ type: 'pong' }));
}
});
</script>Message History
<script>
import { SvelteSocket } from 'svelte-socket';
const socket = new SvelteSocket({ url: 'ws://localhost:8080' });
const sentMessages = $derived(socket.sentMessages);
const receivedMessages = $derived(socket.receivedMessages);
let input = $state('');
function send() {
if (input) {
socket.sendMessage(input);
input = '';
}
}
</script>
<input bind:value={input} />
<button onclick={send}>Send</button>
<h3>Sent ({sentMessages.length})</h3>
{#each sentMessages as { message, timestamp }}
<div>
{new Date(timestamp).toLocaleTimeString()}: {message}
</div>
{/each}
<h3>Received ({receivedMessages.length})</h3>
{#each receivedMessages as { message }}
<div>{message.data}</div>
{/each}
<button onclick={() => socket.clearSentMessages()}>Clear</button>With Provider
<!-- +layout.svelte -->
<script>
import { SocketProvider } from 'svelte-socket';
let { children } = $props();
</script>
<SocketProvider url="ws://localhost:8080">
{@render children()}
</SocketProvider>
<!-- +page.svelte -->
<script>
import { useSocket } from 'svelte-socket';
const socket = useSocket();
socket.addEventListener('message', (event) => {
console.log(event.data);
});
</script>TouchDesigner Integration
Broadcast to all connected clients:
# TouchDesigner Text DAT
def broadcast():
webServer = op('webserver1')
clients = webServer.clients
for client in clients:
webServer.webSocketSendText(client, "message")
print(f"Sent to {len(clients)} clients")Requirements
- Svelte 5.0+
- WebSocket support
License
MIT