React Setup
This guide covers setting up SyncRabbit in a React application (Vite, Create React App, etc.).
Installation
bash
npm install @syncrabbit/reactRunning the Engine
Unlike Next.js, React apps need to run the SyncRabbit engine separately.
Option A: CLI (Recommended for Development)
Run the engine in a separate terminal:
bash
# Terminal 1: Start the engine
DATABASE_URL=postgresql://user:password@host:5432/database npx syncrabbit dev
# Terminal 2: Run your React app
npm run devInitialize the database first (one-time):
bash
DATABASE_URL=postgresql://user:password@host:5432/database npx syncrabbit initOption B: Node SDK (Custom Backend)
If you have an Express, Fastify, or other Node.js backend, you can manage the engine programmatically:
typescript
// server.ts
import express from "express";
import { ensureEngine } from "@syncrabbit/node";
const app = express();
async function start() {
// Start the SyncRabbit engine
const wsUrl = await ensureEngine({
databaseUrl: process.env.DATABASE_URL,
});
console.log(`SyncRabbit ready at ${wsUrl}`);
app.listen(3000, () => {
console.log("Server running on port 3000");
});
}
start();Setup
Add Provider
Wrap your app with the SyncRabbitProvider:
tsx
// main.tsx or App.tsx
import { SyncRabbitProvider } from "@syncrabbit/react";
function App() {
return (
<SyncRabbitProvider url="ws://localhost:4500/ws">
<YourApp />
</SyncRabbitProvider>
);
}TIP
The default URL is ws://localhost:4500/ws. You can omit the url prop if using the default.
Usage
useTable - Subscribe to a Table
tsx
import { useTable } from "@syncrabbit/react";
interface User {
id: number;
name: string;
email: string;
}
function UserList() {
const { data: users, loading, error } = useTable<User>("users", {
initialFetch: async () => {
const res = await fetch("/api/users");
return res.json();
},
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}useWatch - React to Changes
tsx
import { useWatch } from "@syncrabbit/react";
import { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
const res = await fetch("/api/users");
setUsers(await res.json());
};
useEffect(() => {
fetchUsers();
}, []);
// Refetch when users table changes
useWatch("users", fetchUsers);
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Watch Multiple Tables
tsx
useWatch(["orders", "order_items"], () => {
refetchOrders();
});Detailed Change Info
tsx
useWatch(
"users",
(info) => {
console.log(info.operation); // "insert" | "update" | "delete" | "truncate"
console.log(info.row); // The affected row data
},
{ detailed: true }
);useMutation - Write with Instant Invalidation
tsx
import { useMutation } from "@syncrabbit/react";
function CreateUser() {
const { mutate, loading, error } = useMutation({
tables: ["users"],
mutationFn: async (data: { name: string; email: string }) => {
const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
return res.json();
},
});
// After mutate() succeeds, all useWatch callbacks for "users" fire immediately
const handleCreate = () => {
mutate({ name: "Alice", email: "alice@example.com" });
};
return (
<button onClick={handleCreate} disabled={loading}>
Create User
</button>
);
}