React Keepsync Examples
This section shows complete examples of how to use keepsync in React applications. These examples demonstrate a collaborative todo application with real-time synchronization.
Todo Store
A complete Zustand store with keepsync synchronization:
import { sync, DocumentId } from "@tonk/keepsync";
import { create } from "zustand";
// Define the Todo type
export interface Todo {
id: string;
text: string;
completed: boolean;
}
// Define the data structure
interface TodoData {
todos: Todo[];
}
// Define the store state
interface TodoState extends TodoData {
addTodo: (text: string) => void;
toggleTodo: (id: string) => void;
deleteTodo: (id: string) => void;
}
// Create a synced store for todos
export const useTodoStore = create<TodoState>(
sync(
(set) => ({
todos: [],
// Add a new todo
addTodo: (text: string) => {
set((state) => ({
todos: [
...state.todos,
{
id: crypto.randomUUID(),
text,
completed: false,
},
],
}));
},
// Toggle a todo's completed status
toggleTodo: (id: string) => {
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
}));
},
// Delete a todo
deleteTodo: (id: string) => {
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
}));
},
}),
{
// Unique document ID for this store
docId: "todo-list" as DocumentId,
}
)
);
Todo List Component
A React component that displays and manages todos:
import React from "react";
import { Todo, useTodoStore } from "../stores/todoStore";
const TodoList = () => {
const { todos, deleteTodo, toggleTodo } = useTodoStore();
if (todos.length === 0) {
return <p>No todos yet. Add one above!</p>;
}
return (
<ul className="todo-list">
{todos.map((todo: Todo) => (
<li
key={todo.id}
className={`todo-item ${todo.completed ? "completed" : ""}`}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span className="todo-text">{todo.text}</span>
<button className="delete-btn" onClick={() => deleteTodo(todo.id)}>
Delete
</button>
</li>
))}
</ul>
);
};
export default TodoList;
Add Todo Component
A React component for adding new todos:
import React from "react";
import { useState } from "react";
import { useTodoStore } from "../stores/todoStore";
const AddTodo = () => {
const [text, setText] = useState("");
const { addTodo } = useTodoStore();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (text.trim()) {
addTodo(text.trim());
setText("");
}
};
return (
<form onSubmit={handleSubmit} className="add-todo">
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="What needs to be done?"
/>
<button type="submit">Add Todo</button>
</form>
);
};
export default AddTodo;
Main App Component
The main application component that brings everything together:
import React from "react";
import { useState } from "react";
import TodoList from "./components/TodoList";
import AddTodo from "./components/AddTodo";
function App() {
const [connectionStatus, setConnectionStatus] = useState<
"connected" | "disconnected"
>("connected");
// Handle disconnecting from the sync engine
const handleDisconnect = () => {
setConnectionStatus("disconnected");
};
// Handle reconnecting to the sync engine
const handleReconnect = () => {
// This will reinitialize the sync engine on the next store access
setConnectionStatus("connected");
};
return (
<div className="container">
<h1>Collaborative Todo List</h1>
<p>
Status:{" "}
<span
style={{ color: connectionStatus === "connected" ? "green" : "red" }}
>
{connectionStatus}
</span>
{connectionStatus === "connected" ? (
<button onClick={handleDisconnect} style={{ marginLeft: "1rem" }}>
Disconnect
</button>
) : (
<button onClick={handleReconnect} style={{ marginLeft: "1rem" }}>
Reconnect
</button>
)}
</p>
<AddTodo />
<TodoList />
<p>
<small>
Changes are automatically synced across all connected clients.
<br />
Open this app in multiple windows to see real-time collaboration in
action.
</small>
</p>
</div>
);
}
export default App;
Key Concepts Demonstrated
- Sync Middleware: Using
sync()
to create a collaborative Zustand store - Document ID: Using
docId
to identify the shared document - Real-time Updates: Changes are automatically synchronized across all clients
- Component Integration: React components seamlessly use the synced store
- Connection Management: Handling connection status and reconnection scenarios
Running the Example
To run this example:
- Create a new Tonk React app:
tonk create
(choose React) - Replace the generated files with the code above
- Start the development server:
pnpm dev
- Open multiple browser windows to see real-time collaboration
The todos will be synchronized in real-time across all connected clients!