Clients

Clients

Clients are used to get and communicate with actors from your application. Clients can be created from either your frontend or backend.

Creating a Client

Getting an Actor

getOrCreate

Returns a handle to an existing actor or creates one if it doesn’t exist:

const counter = client.counter.getOrCreate("my-counter");
const count = await counter.increment(5);
JavaScript
const counter = useActor({
  name: "counter",
  key: ["my-counter"],
});

// Call actions through the connection
await counter.connection?.increment(5);
React

Pass initialization data when creating:

const game = client.game.getOrCreate("game-123", {
  createWithInput: { gameMode: "tournament", maxPlayers: 8 }
});
JavaScript
const game = useActor({
  name: "game",
  key: ["game-123"],
  createWithInput: { gameMode: "tournament", maxPlayers: 8 },
});
React

get

Returns a handle to an existing actor or null if it doesn’t exist:

const handle = client.myActor.get("actor-id");

if (handle) {
  await handle.someAction();
}
JavaScript
import { createClient } from "rivetkit/client";
import { useMemo } from "react";
import type { registry } from "./registry";

// `get` is not currently supported with useActor.
// Use createClient with useMemo instead:

function MyComponent() {
  const client = useMemo(() => createClient<typeof registry>(), []);
  const handle = useMemo(() => client.myActor.get("actor-id"), [client]);

  // Use handle to call actions
}
React

create

Creates a new actor, failing if one already exists with that key:

const newGame = await client.game.create("game-456", {
  input: { gameMode: "classic" }
});
JavaScript
import { createClient } from "rivetkit/client";
import { useMemo, useEffect, useState } from "react";
import type { registry } from "./registry";

// `create` is not currently supported with useActor.
// Use createClient with useMemo instead:

function MyComponent() {
  const client = useMemo(() => createClient<typeof registry>(), []);
  const [handle, setHandle] = useState(null);

  useEffect(() => {
    client.game.create("game-456", {
      input: { gameMode: "classic" }
    }).then(setHandle);
  }, [client]);

  // Use handle to call actions
}
React

getForId

Connect to an actor using its internal ID:

const actor = client.myActor.getForId("lrysjam017rhxofttna2x5nzjml610");
JavaScript
import { createClient } from "rivetkit/client";
import { useMemo } from "react";
import type { registry } from "./registry";

// `getForId` is not currently supported with useActor.
// Use createClient with useMemo instead:

function MyComponent({ actorId }: { actorId: string }) {
  const client = useMemo(() => createClient<typeof registry>(), []);
  const handle = useMemo(() => client.myActor.getForId(actorId), [client, actorId]);

  // Use handle to call actions
}
React

Prefer using keys over internal IDs for simplicity.

Calling Actions

const counter = client.counter.getOrCreate("my-counter");

const count = await counter.increment(5);
const value = await counter.getCount();
await counter.reset();
JavaScript
const counter = useActor({
  name: "counter",
  key: ["my-counter"],
});

// Call actions through the connection
const count = await counter.connection?.increment(5);
const value = await counter.connection?.getCount();
await counter.connection?.reset();
React

In JavaScript, actions called without connect() are stateless. Each call is independent without a persistent connection. In React, useActor automatically manages a persistent connection.

Connecting to an Actor

For real-time use cases, establish a persistent connection to the actor:

const counter = client.counter.getOrCreate("live-counter");
const conn = counter.connect();

// Listen for events
conn.on("countChanged", (newCount: number) => {
  console.log("Count updated:", newCount);
});

// Call actions through the connection
await conn.increment(1);
JavaScript
const [count, setCount] = useState(0);

const counter = useActor({
  name: "counter",
  key: ["live-counter"],
});

// Listen for events
counter.useEvent("countChanged", (newCount: number) => {
  setCount(newCount);
});

// Call actions through the connection
await counter.connection?.increment(1);
React

Subscribing to Events

Listen for events from connected actors:

const conn = client.chatRoom.getOrCreate("general").connect();

// Listen for events
conn.on("messageReceived", (message) => {
  console.log(`${message.from}: ${message.text}`);
});

// Listen once
conn.once("gameStarted", () => {
  console.log("Game has started!");
});
JavaScript
const [messages, setMessages] = useState<Message[]>([]);

const chatRoom = useActor({
  name: "chatRoom",
  key: ["general"],
});

// Listen for events (automatically cleaned up on unmount)
chatRoom.useEvent("messageReceived", (message) => {
  setMessages((prev) => [...prev, message]);
});
React

Full-Stack Type Safety

Import types from your registry for end-to-end type safety:

import { createClient } from "rivetkit/client";
import type { registry } from "./registry";

const client = createClient<typeof registry>();

// IDE autocomplete shows available actors and actions
const counter = client.counter.getOrCreate("my-counter");
const count = await counter.increment(5);
JavaScript
import { createRivetKit } from "@rivetkit/react";
import type { registry } from "./registry";

const { useActor } = createRivetKit<typeof registry>();

// IDE autocomplete shows available actors and actions
const counter = useActor({ name: "counter", key: ["my-counter"] });
const count = await counter.connection?.increment(5);
React

Use import type to avoid accidentally bundling backend code in your frontend.

Advanced

Disposing Clients & Connections

Dispose clients to close all connections:

await client.dispose();
TypeScript

Dispose individual connections when finished:

const conn = actor.connect();

try {
  conn.on("event", handler);
  await conn.action();
} finally {
  await conn.dispose();
}
TypeScript

When using useActor in React, connections are automatically disposed when the component unmounts. No manual cleanup is required.

Connection Parameters

Pass custom data to the actor when connecting:

const chat = client.chatRoom.getOrCreate("general", {
  params: {
    userId: "user-123",
    displayName: "Alice"
  }
});
JavaScript
const chat = useActor({
  name: "chatRoom",
  key: ["general"],
  params: {
    userId: "user-123",
    displayName: "Alice"
  },
});
React

Authentication

Pass authentication tokens when connecting:

const chat = client.chatRoom.getOrCreate("general", {
  params: {
    authToken: "jwt-token-here"
  }
});
JavaScript
const chat = useActor({
  name: "chatRoom",
  key: ["general"],
  params: {
    authToken: "jwt-token-here"
  },
});
React

See authentication for more details.

Error Handling

import { ActorError } from "rivetkit/client";

const conn = actor.connect();
conn.onError((error: ActorError) => {
  if (error.code === "forbidden") {
    window.location.href = "/login";
  }
});
JavaScript (Connection)
import { ActorError } from "rivetkit/client";

try {
  const result = await actor.protectedAction();
} catch (error) {
  if (error instanceof ActorError && error.code === "forbidden") {
    window.location.href = "/login";
  }
}
JavaScript (Stateless)
import { ActorError } from "rivetkit/client";

const actor = useActor({ name: "myActor", key: ["id"] });

const handleAction = async () => {
  try {
    await actor.connection?.protectedAction();
  } catch (error) {
    if (error instanceof ActorError && error.code === "forbidden") {
      window.location.href = "/login";
    }
  }
};
React

See errors for more details.

Actor Resolution

get and getOrCreate return immediately without making a network request. The actor is resolved lazily when you call an action or connect().

To explicitly resolve an actor and get its ID, use resolve():

const handle = client.counter.getOrCreate("my-counter");
const actorId = await handle.resolve();
console.log(actorId); // "lrysjam017rhxofttna2x5nzjml610"
TypeScript

API Reference