https://github.com/valtiojs/valtio-y
Two-way sync between Valtio proxies and Yjs CRDTs
https://github.com/valtiojs/valtio-y
crdt react yjs
Last synced: 23 days ago
JSON representation
Two-way sync between Valtio proxies and Yjs CRDTs
- Host: GitHub
- URL: https://github.com/valtiojs/valtio-y
- Owner: valtiojs
- License: mit
- Created: 2025-11-01T11:17:44.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-02-12T16:04:28.000Z (4 months ago)
- Last Synced: 2026-04-09T10:06:10.071Z (2 months ago)
- Topics: crdt, react, yjs
- Language: TypeScript
- Homepage:
- Size: 881 KB
- Stars: 21
- Watchers: 1
- Forks: 2
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# valtio-y ⚡
### Effortless Collaborative State
**Write normal JavaScript. Sync in real-time automatically.**
[](https://github.com/valtiojs/valtio-y/actions?query=workflow%3ACI)
[](https://www.npmjs.com/package/valtio-y)
[](https://bundlephobia.com/result?p=valtio-y)
[](https://www.npmjs.com/package/valtio-y)
[](https://www.typescriptlang.org/)
**Build multiplayer apps as easily as writing local state.**
valtio-y synchronizes your [Valtio](https://github.com/pmndrs/valtio) state with [Yjs](https://github.com/yjs/yjs) automatically. It solves the difficult edge cases of state-CRDT syncing—like **array moves**, **item replacements**, and **list reordering**—while remaining significantly more performant than naive bindings.
[Examples](#examples) · [Guides](#guides)
---
## Why valtio-y?
Most CRDT bindings struggle with the "last 10%" of complexity: correctly handling **array moves** (reordering items without deleting/recreating them), **replacing objects** without breaking references, and maintaining **referential equality** for React renders.
valtio-y is a ground-up rewrite focused on **correctness** and **performance**:
- **Solves Hard Sync Problems**: Handles array moves, replacements, and reordering correctly.
- **High Performance**: Optimized for bulk operations and deep state trees; significantly faster than other proxy-based solutions.
- **Production Ready**: Handles the edge cases that usually cause sync divergence in other libraries.
You get automatic conflict resolution, offline support, and efficient re-renders, but with a level of robustness that stands up to complex real-world usage.
**valtio-y handles all of this for you.** Just mutate your state like normal:
```typescript
state.todos.push({ text: "Buy milk", done: false });
state.users[0].name = "Alice";
state.dashboard.widgets[2].position = { x: 100, y: 200 };
// Move item from index 0 to 2 (handled efficiently)
const [todo] = state.todos.splice(0, 1);
state.todos.splice(2, 0, todo);
```
It automatically syncs across all connected users with **zero configuration**. No special APIs, no operational transforms to understand, no conflict resolution code to write.
### Optimized for Performance
valtio-y batches all mutations in a single event loop tick into one Yjs transaction. This means **100 updates result in just 1 network message**.
It also handles large arrays and deep object trees efficiently, only updating the parts of the React component tree that actually changed.
### When to Use valtio-y
**Perfect for:** Real-time collaborative apps involving structured data like **Kanban boards**, **spreadsheets**, **design tools**, **game state**, and **forms**.
**Not for:** Collaborative **text editors** (Google Docs style). For rich text, use [Lexical](https://lexical.dev/), [TipTap](https://tiptap.dev/), or [ProseMirror](https://prosemirror.net/) with their native Yjs bindings.
### Examples
See the power of valtio-y in action:
1. **[Simple App](https://valtio-y-simple.agcty.workers.dev/)** - Objects, arrays, and primitives syncing in real-time.
2. **[Sticky Notes](https://valtio-y-stickynotes.agcty.workers.dev/)** - Production-ready collaborative board.
3. **[Whiteboard](https://valtio-y-whiteboard.agcty.workers.dev)** - Drawing, shapes, and multi-user cursors.
4. **[Todos App](https://valtio-y-todos.agcty.workers.dev)** - Collaborative list management.
5. **[Minecraft Clone](https://valtio-y-minecraft.agcty.workers.dev)** - Multiplayer game state with 3D graphics.
---
## Installation
```bash
# npm
npm install valtio-y valtio yjs
# pnpm
pnpm add valtio-y valtio yjs
# bun
bun add valtio-y valtio yjs
```
---
## Quick Start
Create a synchronized proxy and mutate it like any normal object. Changes automatically sync across clients.
```typescript
import * as Y from "yjs";
import { createYjsProxy } from "valtio-y";
type State = {
text: string;
count: number;
user: { name: string; age: number };
todos: Array<{ text: string; done: boolean }>;
};
// Create a Yjs document
const ydoc: Y.Doc = new Y.Doc();
// Create a synchronized proxy
// getRoot selects which Yjs structure to sync (all clients must use the same name)
const { proxy: state } = createYjsProxy(ydoc, {
getRoot: (doc) => doc.getMap("root"), // Most apps use one root Map
});
// Mutate state like a normal object
state.text = "hello";
state.count = 0;
// Nested objects work too
state.user = { name: "Alice", age: 30 };
state.user.age = 31;
// Arrays work naturally
state.todos = [{ text: "Learn valtio-y", done: false }];
state.todos.push({ text: "Build something cool", done: false });
state.todos[0].done = true;
```
That's it! State is now synchronized via Yjs. Add a provider to sync across clients.
---
## Key Features
- **⚡ Zero API Overhead** - No special methods—just mutate objects like normal JavaScript
- **🎯 Fine-Grained Updates** - Valtio ensures only components with changed data re-render.
- **🌐 Offline-First** - Local changes automatically merge when reconnected
- **🛡️ Production-Ready** - Validation, rollback, comprehensive tests, and benchmarks
- **🔒 Type-Safe** - Full TypeScript support with complete type inference
- **🔌 Provider-Agnostic** - Works with any Yjs provider (WebSocket, WebRTC, IndexedDB)
---
## Using with React
Bind your components with Valtio's `useSnapshot` hook. Components re-render only when their data changes:
```jsx
import { useSnapshot } from "valtio/react";
function TodoList() {
const snap = useSnapshot(state);
return (
-
(state.todos[i].done = !state.todos[i].done)}
/>
{todo.text}
{snap.todos.map((todo, i) => (
))}
);
}
```
**Key principle:** Read from the snapshot (`snap`), mutate the proxy (`state`).
valtio-y works with any framework that Valtio supports: React, Vue, Svelte, Solid, and vanilla JavaScript.
**For optimizing large lists** with thousands of items, see the [Performance Guide](./guides/performance-guide.md#optimizing-lists).
**Note for text inputs:** When using controlled text inputs (like `` or ``), add `{ sync: true }` to prevent cursor jumping:
```jsx
const snap = useSnapshot(state, { sync: true });
(state.text = e.target.value)} />;
```
This forces synchronous updates instead of Valtio's default async batching. See [Valtio issue #270](https://github.com/pmndrs/valtio/issues/270) for details.
---
## Guides
Core documentation for understanding and using valtio-y effectively:
- **[Getting Started](./guides/getting-started.md)** - Essential patterns for collaboration, initialization, and React integration
- **[Basic Operations](./guides/basic-operations.md)** - Objects, arrays, and nested structures
- **[Core Concepts](./guides/concepts.md)** - Understand CRDTs and the valtio-y mental model
- **[Structuring Your App](./guides/structuring-your-app.md)** - How to organize state with `getRoot`
- **[Undo/Redo](./guides/undo-redo.md)** - Implement undo/redo with Yjs UndoManager
- **[Performance Guide](./guides/performance-guide.md)** - Batching, bulk operations, and optimization
---
## Common Patterns
### Setting Up Collaboration
Connect a Yjs provider to sync across clients:
```js
import { WebsocketProvider } from "y-websocket";
const provider = new WebsocketProvider("ws://localhost:1234", "room", ydoc);
// State syncs automatically
```
Works with [y-websocket](https://github.com/yjs/y-websocket), [y-partyserver](https://github.com/partykit/partykit/tree/main/packages/y-partyserver), [y-webrtc](https://github.com/yjs/y-webrtc), [y-indexeddb](https://github.com/yjs/y-indexeddb), and more.
**→ See [Getting Started Guide](./guides/getting-started.md) for initialization patterns and provider setup**
### Working with State
```js
// Arrays - all standard methods work
state.items.push(newItem);
state.items[0] = updatedItem;
// Objects - mutate naturally
state.user.name = "Alice";
state.settings = { theme: "dark" };
// Access anywhere (event handlers, timers, async functions)
state.count++;
```
**→ See [Basic Operations](./guides/basic-operations.md) for arrays, objects, and nested structures**
### Undo/Redo
```js
const {
proxy: state,
undo,
redo,
} = createYjsProxy(ydoc, {
getRoot: (doc) => doc.getMap("state"),
undoManager: true, // Enable undo/redo
});
undo(); // Undo last change
redo(); // Redo
```
**→ See [Undo/Redo Guide](./guides/undo-redo.md) for full integration with React and advanced patterns**
---
## Performance
valtio-y is fast out of the box with automatic batching, bulk operations, and efficient proxy creation. Typical performance characteristics:
| Operation | Time | Notes |
| --------------------------- | -------- | -------------------------- |
| Small updates (1-10 items) | ~1-3ms | Typical UI interactions |
| Bulk operations (100 items) | ~3-8ms | Automatically optimized |
| Large arrays (1000 items) | ~15-30ms | Bootstrap/import scenarios |
| Deep nesting (10+ levels) | ~2-4ms | Cached proxies stay fast |
**→ See [Performance Guide](./guides/performance-guide.md) for benchmarking, optimization patterns, and React integration**
---
## Limitations
### Not Supported
- `undefined` values (use `null` or delete the key)
- Non-serializable types (functions, symbols, class instances)
- Direct length manipulation (use `array.splice()` instead of `array.length = N`)
### What Works
- Objects and arrays with full support for deep nesting
- Primitives: string, number, boolean, null
- All array methods: push, pop, splice, and more
- Undo/redo via Yjs UndoManager
### Research In Progress
**Important:** valtio-y is designed for **shared application state** (collaborative data structures like objects, arrays, and primitives), not for building text editors.
**If you're building a text editor:** Use the native Yjs integration for your editor:
- [Lexical](https://lexical.dev/) → Use `@lexical/yjs`
- [TipTap](https://tiptap.dev/) → Use their built-in Yjs extension
- [ProseMirror](https://prosemirror.net/) → Use `y-prosemirror`
These editors have specialized Yjs integrations optimized for their specific use cases.
**Collaborative text integration research:**
valtio-y currently focuses on collaborative data structures like maps, arrays, and primitives. Y.Text and Y.Xml\* nodes are **not** part of the supported surface area today because plain strings inside shared objects have covered the real-world use cases we've seen so far. We're still tinkering with richer text and XML nodes on the `research/ytext-integration` branch, so if you rely on those leaf types we'd love to hear what you're building.
**Current status:**
- Core types (Y.Map, Y.Array, primitives) are production-ready with clean, well-tested implementations
- Notes from the collaborative text/XML prototype remain in the `research/ytext-integration` branch for anyone curious about the trade-offs we explored
**Have a use case for collaborative text in shared state?** We'd love to learn more! Please [open an issue](https://github.com/valtiojs/valtio-y/issues) to discuss your requirements.
---
## Best Practices
**Do:**
- Use `bootstrap()` when initializing state with network sync providers
- Batch related updates in the same tick for better performance
- Use bulk array operations (`push(...items)`) instead of loops
- Cache references to deeply nested objects in tight loops
- Store text fields as plain strings
**Don't:**
- Use `undefined` values (use `null` or delete the property)
- Store functions or class instances (not serializable)
- Manipulate `array.length` directly (use `splice()` instead)
**→ See [Performance Guide](./guides/performance-guide.md) for advanced patterns like concurrent list reordering with fractional indexing**
---
**Feedback and contributions welcome!** If you find bugs or have suggestions, please [open an issue](https://github.com/valtiojs/valtio-y/issues).
For detailed technical documentation, see:
- [Architecture](./docs/architecture/architecture.md)
- [Limitations](./docs/architecture/limitations.md)
- [Data Flow](./docs/architecture/data-flow.md)