Remote-functions DX for realtime

WebSockets that feel like SvelteKit.

Like remote functions, the contract lives beside the route and the page imports it directly. Unlike remote functions, it stays connected: Socket.IO powers rooms, emits, listeners, and acknowledgements under the hood.

Lives beside the route

Socket.IO hidden underneath

Generated typed client

chat/[roomId]/chat.socket.ts

route contract

// src/routes/chat/[roomId]/chat.socket.ts
import { event, room, socket } from 'liverpc/server';

export const chat = socket({
  // The [roomId] URL param chooses the private room.
  rooms: { chat: room.private(({ params }) => ['chat', params.roomId]) },
  fromClient: {
    // Validate payloads, then broadcast to the room.
    send: event(messageSchema, ({ rooms, data }) =>
      rooms.chat.emit.message(data)
    )
  }
});

page import

// +page.svelte
import { chat } from './chat.socket';

// Typed realtime client for this page.
const room = chat();

// Opens Socket.IO when the listener is used.
room.on.message((message) => (messages = [message, ...messages]));

// Payload inferred from .socket.ts.
await room.emit.send({ text: 'hello' });

Install for SvelteKit

Pick your package manager. The init command patches Vite and creates the socket hook.

# install the realtime package
pnpm add liverpc

# patch Vite and create hooks.socket.ts
pnpm exec liverpc init
CLI setup Vite plugin hooks.socket.ts

Why it is cleaner

Remote-style imports

A page imports the .socket.ts file beside it, then calls typed emits and listeners from browser code.

Server-owned params and auth

Choose rooms from route params, cookies, and sessions on the server instead of trusting browser join requests.

Socket.IO without the glue

Use rooms, broadcasts, and acknowledgements while LiveRPC handles the Socket.IO client and server setup.

Realtime as a route capability

Keep HTTP pages, route params, and websocket behavior in one mental model instead of a separate socket layer.