TypeSafe Primitives for a Realtime Web
Open-source, multiplayer APIs powered-by TypeScript inference end-to-end
// backend const sendMessage = io.procedure .input(z.object({ message: z.string() })) .broadcast(({ message }) => ({ receiveMessage: { message }, })); const router = io.router({ sendMessage }); const ioServer = io.server({ router }); // frontend event.receiveMessage.useEvent(({ data }) => { console.log(data.message); }); broadcast.sendMessage({ message: "Hello!" });
Automatic type-safety
Leverage the full-power of TypeScript interfence to catch errors quickly and get IDE auto-completions as you're developing!
// pnpm install @pluv/platform-node createIO(platformNode()); // pnpm install @pluv/platform-cloudflare createIO(platformCloudflare());
const GET = async (req: Request) => { const room = getRoomFromUrl(req.url); const { user } = await getSession(req); const token = await ioServer .createToken({ room, user }); return new Response(token, { status: 200 }); };
const { useBroadcast, useOthers, // ... } = createBundle(client); const broadcast = useBroadcast(); const names = useOthers((others) => { return others.map(({ user }) => user.name); });
// pnpm install @pluv/platform-pluv const io = createIO( platformPluv({ authorize: { user: z.object({ id: z.string() }), }, basePath: "/api/pluv", publicKey: "pk_...", secretKey: "sk_...", webhookSecret: "whsec_...", }) );
Usage monitoring
When hosted on the pluv.io network, track active rooms and connections, and custom events to gain insights on how your app is being used.
const client = createClient({ types, initialStorage: yjs.doc(() => ({ messages: yjs.array<string>([]), })), }); const [messages, type] = useStorage("messages"); const addMessage = (message: string) => { type?.push([message]); }; messages?.map((message, i) => ( <div key={i}>{message}</div> ));
const client = createClient({ types, presence: z.object({ cursor: z.object({ x: z.number(), y: z.number(), }).nullable(), }), }); const [myCursor] = useMyPresence( ({ cursor }) => cursor, ); const cursors = useOthers((others) => ( others.map(({ presence }) => presence.cursor) ));
// backend const sendMessage = io.procedure .input(z.object({ message: z.string() })) .broadcast(({ message }) => ({ receiveMessage: { message }, })); const router = io.router({ sendMessage }); const ioServer = io.server({ router }); // frontend event.receiveMessage.useEvent(({ data }) => { console.log(data.message); }); broadcast.sendMessage({ message: "Hello!" });
Automatic type-safety
Leverage the full-power of TypeScript interfence to catch errors quickly and get IDE auto-completions as you're developing!
const client = createClient({ types, initialStorage: yjs.doc(() => ({ messages: yjs.array<string>([]), })), }); const [messages, type] = useStorage("messages"); const addMessage = (message: string) => { type?.push([message]); }; messages?.map((message, i) => ( <div key={i}>{message}</div> ));
// pnpm install @pluv/platform-pluv const io = createIO( platformPluv({ authorize: { user: z.object({ id: z.string() }), }, basePath: "/api/pluv", publicKey: "pk_...", secretKey: "sk_...", webhookSecret: "whsec_...", }) );
const GET = async (req: Request) => { const room = getRoomFromUrl(req.url); const { user } = await getSession(req); const token = await ioServer .createToken({ room, user }); return new Response(token, { status: 200 }); };
const { useBroadcast, useOthers, // ... } = createBundle(client); const broadcast = useBroadcast(); const names = useOthers((others) => { return others.map(({ user }) => user.name); });
// pnpm install @pluv/platform-node createIO(platformNode()); // pnpm install @pluv/platform-cloudflare createIO(platformCloudflare());
const client = createClient({ types, presence: z.object({ cursor: z.object({ x: z.number(), y: z.number(), }).nullable(), }), }); const [myCursor] = useMyPresence( ({ cursor }) => cursor, ); const cursors = useOthers((others) => ( others.map(({ presence }) => presence.cursor) ));
Usage monitoring
When hosted on the pluv.io network, track active rooms and connections, and custom events to gain insights on how your app is being used.
Automatic type-safety
Leverage the full-power of TypeScript interfence to catch errors quickly and get IDE auto-completions as you're developing!
Usage monitoring
When hosted on the pluv.io network, track active rooms and connections, and custom events to gain insights on how your app is being used.
Developer-Focused APIs
Unlock powerful utilities to build complex multiplayer experiences in minutes, not days.
Create your PluvIO server
To get started with pluv.io, you will first need to create a PluvIO
server that can start registering new realtime connections.
Pick which platform (i.e. adapter) you wish to connect to. Connect to pluv.io as a fully-managed service for your Next.js app. Or self-host on Node.js or on Cloudflare Workers.
Define a Zod schema validator to ensure the structure and type-safety of authorized users in your rooms.
Set-up HTTP endpoints
Next, set-up custom authorization for your rooms. And mount the `PluvServer` to enable realtime connections to your pluv.io instance.
Setup will vary depending on the platform (e.g. Node.js or Cloudflare Workers) used.
Prepare your frontend bundle
ioServer
. This frontend bundle contains all of pluv.io's APIs for realtime collaboration.You can optionally unlock more realtime capabilities for your app by defining:
- A presence state for each user with Zod.
- CRDT storage with Yjs or Loro.
- Custom typesafe events on your backend[1] or your frontend.
Wrap with PluvRoomProvider
The room bundle provides a PluvRoomProvider
to wrap your page with. Once you do, pluv.io APIs can now be used in nested components!
Build with typesafe realtime primitives!
With our frontend bundle ready to use, you can start using pluv.io realtime primitives with TypeScript autocompletion and intellisense for your custom events, presence and storage.
Type definitions will be as narrow as you've configured, with minimal manual type definitions and without code-generation!