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

  1. Sync Middleware: Using sync() to create a collaborative Zustand store
  2. Document ID: Using docId to identify the shared document
  3. Real-time Updates: Changes are automatically synchronized across all clients
  4. Component Integration: React components seamlessly use the synced store
  5. Connection Management: Handling connection status and reconnection scenarios

Running the Example

To run this example:

  1. Create a new Tonk React app: tonk create (choose React)
  2. Replace the generated files with the code above
  3. Start the development server: pnpm dev
  4. Open multiple browser windows to see real-time collaboration

The todos will be synchronized in real-time across all connected clients!