LogoRobo.js

@robojs/sync

Real-time state sync across clients for multiplayer games and apps.

Install @robojs/sync with:

Real-time state synchronization across clients using WebSockets. Ideal for multiplayer games, collaborative apps, and live dashboards.

const [position, setPosition, context] = useSyncState({ x: 0, y: 0 }, [channelId])
const [position, setPosition, context] = useSyncState({ x: 0, y: 0 }, [channelId])

Works like React's useState, but syncs state across all clients in real-time via WebSockets. Components sharing the same key array see the same state. No manual socket handling required. Requires @robojs/server as a peer dependency.

Features

Shared State

useSyncState syncs state like React's useState

Client Awareness

Track connected clients and host status

Broadcasts

Fire-and-forget ephemeral messaging

RPC Calls

Server-authoritative game logic

Zones

Hierarchical key namespacing with SyncZone

SyncBox

Locking, interpolation, and optimistic updates

Cursors

Collaborative cursor tracking

Draggables

Synchronized drag-and-drop

Presence

Participant tracking with stale detection

Server Handlers

File-based validation, transformation, and RPC

Schema Validation

Built-in schema format or Zod integration

Flashcore Realtime

Realtime subscriptions for Flashcore models

Installation

Add the plugin to your Robo project:

npx robo add @robojs/sync@next

@robojs/server is also required. Install it with npx robo add @robojs/server if you haven't already.

To scaffold a new project with sync preconfigured:

npx create-robo@next my-project -p @robojs/sync@next

Setup

Wrap your app with SyncContextProvider to establish the WebSocket connection and make sync hooks available.

src/app/App.tsx
import { SyncContextProvider } from '@robojs/sync'

export function App() {
	return (
		<SyncContextProvider clientData={{ name: 'Player1' }} loadingScreen={<Loading />}>
			<Activity />
		</SyncContextProvider>
	)
}
src/app/App.jsx
import { SyncContextProvider } from '@robojs/sync'

export function App() {
	return (
		<SyncContextProvider clientData={{ name: 'Player1' }} loadingScreen={<Loading />}>
			<Activity />
		</SyncContextProvider>
	)
}
PropDescription
clientDataMetadata attached to the current client. Other clients see this via client.data.
loadingScreenComponent shown while the WebSocket connection is being established.

Scaling beyond sync

For complex multiplayer games that need authoritative server state, matchmaking, reconnection, and deterministic logic, consider Colyseus. Model your sync keys as Colyseus rooms and move update logic to server-side room handlers.

Next Steps

On this page