Skip to content

Apps

An App bundles one or more actor definitions into a single object that the server, client, and React packages can consume.

import { createApp } from "@zocket/core";
import { ChatRoom } from "./chat";
import { GameMatch } from "./game";
export const app = createApp({
actors: {
chat: ChatRoom,
game: GameMatch,
},
});

The keys you choose (chat, game) become the actor names used everywhere:

  • Server — the handler routes RPC calls to the correct actor by name
  • Clientclient.chat("room-1") returns a typed handle for the chat actor
  • ReactuseActor("chat", "room-1") uses the same name

The AppDef type carries the full type information of all registered actors. Pass typeof app as a generic to the client and React factories:

import { createClient } from "@zocket/client";
import type { app } from "./server";
// Full type inference — no manual types needed
const client = createClient<typeof app>({ url: "ws://localhost:3000" });
const room = client.chat("general");
// ^? ActorHandle with sendMessage, on("newMessage"), state, etc.

The returned object has this shape:

interface AppDef<TActors> {
readonly _tag: "AppDef";
readonly actors: TActors; // Record<string, ActorDef>
}

The _tag discriminant is used internally for type narrowing. You generally don’t need to interact with it directly.

  • Use camelCase for actor names: chat, gameMatch, drawingRoom
  • Each name must be unique within an app
  • The name is used as-is in the wire protocol ({ actor: "chat", actorId: "room-1" })