sse-crossframework
v1.0.2
Published
Framework-agnostic SSE toolkit with React, Vue, and Solid adapters
Maintainers
Readme
sse-crossframework
Multi-framework SSE package with adapters for:
sse-crossframework/reactsse-crossframework/vuesse-crossframework/solid
Hook return value
Hooks return an object aligned with SSEReturn (see sse-crossframework/shared):
status:'connecting' | 'connected' | 'disconnected' | 'error' | 'closed'lastEvent: lastSSEEventornullevents: array of all receivedSSEEventserror:Error | nullretryCount: current retry attempt numberconnect,close,reconnect: control the connection
React spreads the latest snapshot on each update. Vue exposes reactive refs for state fields (lastEvent and events use shallowRef so large event lists are not deep-reactive and generics stay accurate). Solid exposes signals for state fields.
Reactive URL, options, and worker path (Vue & Solid)
Vue hooks accept MaybeRefOrGetter for endpointUrl, options, and (where applicable) workerPath: you can pass a string, a ref, or a getter (() => ...). Solid hooks accept MaybeAccessor (string or Accessor). When any of these values change, the previous client is destroy()’d and a new connection is created—no stale worker port or EventSource left behind.
Keep options referentially stable when the contents did not change (React: useMemo; Vue: computed / shallowRef + assign; Solid: createMemo or a store) to avoid tearing down the client every render. When debug is enabled, the library can warn if options identity churns while the hook is active.
close() vs destroy()
SSEClient:close()stops the underlyingEventSourceand clears timers; usedestroy()when you are done with the instance (same cleanup path).SSESharedWorkerClient:close()tells the Shared Worker to disconnect the SSE stream for this client and resets the local snapshot; it does not close the browser MessagePort or remove listeners.destroy()unsubscribes from the worker, closes the port, clears listeners, and cancels any scheduled delayedautoConnect. The React/Vue/Solid hooks calldestroy()in effect cleanup on unmount so the worker connection is fully released.
Shared Worker setup
This package ships a ready-to-use Shared Worker. After install, copy it into your app’s static public directory so the browser can load it by URL.
Install and copy the worker
- Install the package
npm install sse-crossframework
# or
yarn add sse-crossframework- Copy the Shared Worker into
public
npx sse-crossframework init-workerBy default the file is written to:
public/shared-worker.jsTo use a custom path:
npx sse-crossframework init-worker public/sse/shared-worker.jsServe that file at the same path you pass as the worker URL argument to hooks (e.g. /sse/shared-worker.js if you copied under public/sse/).
React
useSSEWithSharedWorker
import { useSSEWithSharedWorker } from 'sse-crossframework/react';
function MyComponent() {
const { lastEvent, events, status, error, retryCount, connect, close } =
useSSEWithSharedWorker(
'/api/events',
{ /* SSEOptions */ },
'/shared-worker.js' // URL path to the worker in public
);
if (status === 'connecting') return <div>Connecting…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<button type="button" onClick={connect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<div>Status: {status}</div>
<div>Retries: {retryCount}</div>
<pre>{JSON.stringify(lastEvent, null, 2)}</pre>
<pre>{JSON.stringify(events, null, 2)}</pre>
</div>
);
}useSSE
import { useSSE } from 'sse-crossframework/react';
function MyComponent() {
const { lastEvent, events, status, error, connect, close } = useSSE(
'/api/events',
{ /* SSEOptions */ }
);
if (status === 'connecting') return <div>Connecting…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<button type="button" onClick={connect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<pre>{JSON.stringify(lastEvent, null, 2)}</pre>
<pre>{JSON.stringify(events, null, 2)}</pre>
</div>
);
}useSSEAdaptive (auto / shared-worker fallback)
import { useSSEAdaptive } from 'sse-crossframework/react';
function MyComponent() {
const { lastEvent, events, status, error, connect, close, reconnect } =
useSSEAdaptive(
'/api/events',
{ /* SSEOptions */ },
'/shared-worker.js' // worker URL in public
);
return (
<div>
<button type="button" onClick={connect}>
Connect
</button>
<button type="button" onClick={reconnect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<div>Status: {status}</div>
{error && <div>Error: {error.message}</div>}
<pre>{JSON.stringify(lastEvent, null, 2)}</pre>
<pre>{JSON.stringify(events, null, 2)}</pre>
</div>
);
}Vue
useSSEWithSharedWorker
<script setup>
import { useSSEWithSharedWorker } from 'sse-crossframework/vue';
const { lastEvent, events, status, error, retryCount, connect, close } =
useSSEWithSharedWorker(
'/api/events',
{},
'/shared-worker.js'
);
</script>
<template>
<button type="button" @click="connect">Reconnect</button>
<button type="button" @click="close">Disconnect</button>
<div>Status: {{ status }}</div>
<div>Retries: {{ retryCount }}</div>
<div v-if="error">Error: {{ error.message }}</div>
<pre>{{ lastEvent }}</pre>
<pre>{{ events }}</pre>
</template>useSSE
<script setup>
import { useSSE } from 'sse-crossframework/vue';
const { lastEvent, events, status, error, connect, close } = useSSE(
'/api/events',
{}
);
</script>
<template>
<button type="button" @click="connect">Reconnect</button>
<button type="button" @click="close">Disconnect</button>
<div>Status: {{ status }}</div>
<div v-if="error">Error: {{ error.message }}</div>
<pre>{{ lastEvent }}</pre>
<pre>{{ events }}</pre>
</template>useSSEAdaptive
<script setup>
import { useSSEAdaptive } from 'sse-crossframework/vue';
const { lastEvent, events, status, error, connect, close, reconnect } =
useSSEAdaptive('/api/events', {}, '/shared-worker.js');
</script>
<template>
<button type="button" @click="connect">Connect</button>
<button type="button" @click="reconnect">Reconnect</button>
<button type="button" @click="close">Disconnect</button>
<div>Status: {{ status }}</div>
<div v-if="error">Error: {{ error.message }}</div>
<pre>{{ lastEvent }}</pre>
<pre>{{ events }}</pre>
</template>SolidJS
useSSEWithSharedWorker
import { useSSEWithSharedWorker } from 'sse-crossframework/solid';
function MyComponent() {
const { lastEvent, events, status, error, retryCount, connect, close } =
useSSEWithSharedWorker('/api/events', {}, '/shared-worker.js');
return (
<>
<button type="button" onClick={connect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<div>Status: {status()}</div>
<div>Retries: {retryCount()}</div>
{error() && <div>Error: {error().message}</div>}
<pre>{JSON.stringify(lastEvent(), null, 2)}</pre>
<pre>{JSON.stringify(events(), null, 2)}</pre>
</>
);
}useSSE
import { useSSE } from 'sse-crossframework/solid';
function MyComponent() {
const { lastEvent, events, status, error, connect, close } = useSSE(
'/api/events',
{}
);
return (
<>
<button type="button" onClick={connect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<div>Status: {status()}</div>
{error() && <div>Error: {error().message}</div>}
<pre>{JSON.stringify(lastEvent(), null, 2)}</pre>
<pre>{JSON.stringify(events(), null, 2)}</pre>
</>
);
}useSSEAdaptive
import { useSSEAdaptive } from 'sse-crossframework/solid';
function MyComponent() {
const { lastEvent, events, status, error, connect, close, reconnect } =
useSSEAdaptive('/api/events', {}, '/shared-worker.js');
return (
<>
<button type="button" onClick={connect}>
Connect
</button>
<button type="button" onClick={reconnect}>
Reconnect
</button>
<button type="button" onClick={close}>
Disconnect
</button>
<div>Status: {status()}</div>
{error() && <div>Error: {error().message}</div>}
<pre>{JSON.stringify(lastEvent(), null, 2)}</pre>
<pre>{JSON.stringify(events(), null, 2)}</pre>
</>
);
}Note: If you used a custom output path with init-worker, pass the same public URL as the worker argument to useSSEWithSharedWorker / useSSEAdaptive (for example /sse/shared-worker.js).
