Typed, component-first live updates for SvelteKit. ActionBus gives an app one shared WebSocket connection, typed channel subscriptions, authorized server broadcasts, and Svelte stores for client-side reactive state.
V1 is intentionally server-to-client for business data. Client WebSocket messages are limited to subscribe/unsubscribe control messages; mutations should remain SvelteKit actions or endpoints.
- One root Svelte 5
<ActionBus>provider - Typed channels and per-channel events through
App.ActionEvents - Runtime channel/event-name validation generated by the Vite plugin
- Authorized server-side subscriptions
- Reference-counted client subscriptions
- Svelte readable stores, subscription-scoped errors, and reducer-based
eventStore - Best-effort delivery with reconnect and automatic resubscribe
npm install @sourceregistry/sveltekit-actionbusRegister the Vite plugin.
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { actionbus } from '@sourceregistry/sveltekit-actionbus/vite';
export default defineConfig({
plugins: [sveltekit(), actionbus()]
});Declare the channels and events your app can use.
// src/app.d.ts
import type { Action } from '@sourceregistry/sveltekit-actionbus';
declare global {
namespace App {
interface ActionEvents {
'project:${string}': {
'task.updated': Action<{ id: string; title: string }>;
'task.deleted': Action<{ id: string }>;
};
'user:${string}:notifications': {
'notification.created': Action<{ id: string; message: string }>;
};
}
}
}
export {};Mount one bus near the root of the app.
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import ActionBus from '@sourceregistry/sveltekit-actionbus/ActionBus.svelte';
let { children } = $props();
</script>
<ActionBus url="/actionbus">
{@render children()}
</ActionBus>Subscribe from descendants with the module export from ActionBus.svelte.
<script lang="ts">
import { subscribe } from '@sourceregistry/sveltekit-actionbus/ActionBus.svelte';
const project = subscribe(['project:123'] as const);
const errors = project.errors;
const tasks = project.eventStore(data.tasks, {
'task.updated': (tasks, message) => {
if (message.channel !== 'project:123') return tasks;
return upsert(tasks, message.event.payload);
},
'task.deleted': (tasks, message) => {
if (message.channel !== 'project:123') return tasks;
return tasks.filter((task) => task.id !== message.event.payload.id);
}
});
</script>You can also use the convenience component.
<script lang="ts">
import ActionSubscription from '@sourceregistry/sveltekit-actionbus/ActionSubscription.svelte';
</script>
<ActionSubscription channels={['project:123'] as const}>
{#snippet children({ events, errors, eventStore, state })}
<!-- render with the subscription stores -->
{/snippet}
</ActionSubscription>Create one server-side bus and import it from server code that broadcasts events.
// src/lib/server/actionbus.ts
import { createActionBus } from '@sourceregistry/sveltekit-actionbus/server';
export const actionbus = createActionBus({
path: '/actionbus',
authorize: async ({ channel, request }) => {
return canSubscribe(request, channel);
}
});Broadcast typed events to subscribed clients.
actionbus.broadcast('project:123', {
type: 'task.updated',
payload: { id: 't1', title: 'New title' }
});This repository includes a runnable showcase under src/routes.
npm run devOpen the local app in two browser tabs. The page subscribes to project:demo; submitting the form in one tab broadcasts a typed task.updated event to the other tab through the shared ActionBus socket.
ActionBus uses WebSockets through @sourceregistry/sveltekit-websockets. It needs a SvelteKit deployment target that supports persistent WebSocket upgrades. Serverless or edge-only platforms that do not support WebSocket upgrades are out of scope for V1.