website/src/content/docs/actors/quickstart/cloudflare.mdx
Set up a Rivet project locally that runs on Cloudflare Workers. The @rivetkit/cloudflare-workers package wires the WebAssembly runtime for you.
npm install rivetkit @rivetkit/cloudflare-workers
npm install --save-dev wrangler
name = "rivetkit-cloudflare"
main = "src/index.ts"
compatibility_date = "2025-04-01"
compatibility_flags = ["nodejs_compat"]
The nodejs_compat flag is required so the runtime can read its connection config from process.env.
createHandler serves the Rivet manager API on /api/rivet. Pick how you want to handle your own routes:
npm install hono).fetch and route requests yourself.import { actor } from "rivetkit";
import { createHandler } from "@rivetkit/cloudflare-workers";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
export default createHandler({ use: { counter } });
import { actor } from "rivetkit";
import { createClient } from "rivetkit/client";
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
import { Hono } from "hono";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
// `setup` returns a typed registry, so the client below is fully typed.
const registry = setup({ use: { counter } });
const app = new Hono();
app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!"));
app.post("/increment/:name", async (c) => {
// `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet
// dev`, or set as a Worker secret in production).
const client = createClient<typeof registry>();
const count = await client.counter.getOrCreate(c.req.param("name")).increment(1);
return c.json({ count });
});
// Rivet keeps `/api/rivet`; Hono handles everything else.
export default createHandler(registry, { fetch: app.fetch });
import { actor } from "rivetkit";
import { createClient } from "rivetkit/client";
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
// `setup` returns a typed registry, so the client below is fully typed.
const registry = setup({ use: { counter } });
// Rivet keeps `/api/rivet`; your `fetch` handles everything else.
export default createHandler(registry, {
fetch: async (request: Request) => {
const url = new URL(request.url);
if (url.pathname === "/") {
return new Response("Hello from a raw Rivet Worker router!");
}
const increment = url.pathname.match(/^\/increment\/(.+)$/);
if (request.method === "POST" && increment) {
// `createClient` reads RIVET_ENDPOINT from the environment (passed by
// `rivet dev`, or set as a Worker secret in production).
const client = createClient<typeof registry>();
const count = await client.counter.getOrCreate(increment[1]).increment(1);
return Response.json({ count });
}
return new Response("Not found", { status: 404 });
},
});
Start Rivet. The CLI runs the local engine and spawns wrangler dev for you:
npx @rivetkit/cli dev --provider cloudflare
Visit http://localhost:6420 in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
</Step> <Step title="Connect To The Rivet Actor">This code can run either in your frontend or within your backend. It connects directly to the local engine on http://localhost:6420:
export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
registry.start();
```ts client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./index";
const client = createClient<typeof registry>("http://localhost:6420");
// Get or create a counter actor for the key "my-counter"
const counter = client.counter.getOrCreate(["my-counter"]);
// Call actions
const count = await counter.increment(3);
console.log("New count:", count);
// Listen to realtime events
const connection = counter.connect();
connection.on("newCount", (newCount: number) => {
console.log("Count changed:", newCount);
});
// Increment through connection
await connection.increment(1);
See the JavaScript client documentation for more information.
</Tab> <Tab title="React"> <CodeGroup workspace> ```ts index.ts @hide import { actor, setup } from "rivetkit";export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
registry.start();
```tsx Counter.tsx
import { createRivetKit } from "@rivetkit/react";
import { useState } from "react";
import type { registry } from "./index";
const { useActor } = createRivetKit<typeof registry>("http://localhost:6420");
function Counter() {
const [count, setCount] = useState(0);
// Get or create a counter actor for the key "my-counter"
const counter = useActor({
name: "counter",
key: ["my-counter"]
});
// Listen to realtime events
counter.useEvent("newCount", (x: number) => setCount(x));
const increment = async () => {
// Call actions
await counter.connection?.increment(1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
See the React documentation for more information.
</Tab> </Tabs> </Step> <Step title="Deploy">Ready to ship? See Deploying to Cloudflare Workers.
</Step> </Steps>