Remote-style imports
A page imports the .socket.ts file beside it, then calls typed emits and listeners from browser code.
Remote-functions DX for realtime
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
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' });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 initA page imports the .socket.ts file beside it, then calls typed emits and listeners from browser code.
Choose rooms from route params, cookies, and sessions on the server instead of trusting browser join requests.
Use rooms, broadcasts, and acknowledgements while LiveRPC handles the Socket.IO client and server setup.
Keep HTTP pages, route params, and websocket behavior in one mental model instead of a separate socket layer.