Zustand Yjs Middleware
zustand-middleware-yjs is a Zustand middleware that enables real-time collaborative state synchronization for any Zustand store using Yjs. This library, currently at stable version 1.3.1 (last updated June 2023), transforms a standard Zustand store into a Conflict-free Replicated Data Type (CRDT), ensuring state consistency across multiple peers. Its key differentiator is its ability to wrap an existing Zustand store creator, making any store collaborative without requiring special hooks or structures for shared types, in contrast to libraries like `zustand-yjs`. It integrates seamlessly with vanilla Zustand and can be composed with other Zustand middleware such as Immer or Redux, broadening its applicability across various React and non-React environments where Zustand is used. The package ships with TypeScript types, but its development appears to be in maintenance mode as of mid-2026.
Common errors
-
Error: 'awareness' is not a property of the Y.Doc provided to zustand-middleware-yjs.
cause Attempting to access or synchronize Yjs awareness state directly through the Zustand store wrapped by this middleware.fixThe `zustand-middleware-yjs` library explicitly does not support the Yjs awareness protocol. Implement awareness management outside of the Zustand store, directly using the `Y.Doc` and a separate state mechanism if needed. -
TypeError: Cannot read properties of undefined (reading 'observeDeep')
cause An incorrect `Y.Doc` instance or shared type name was passed to the `yjs` middleware, or the `Y.Doc` was not properly initialized.fixEnsure that a valid `Y.Doc` instance is provided as the first argument to `yjs(ydoc, 'shared-type-name', ...)` and that the shared type name ('shared-type-name' in the example) is a string.
Warnings
- gotcha The Yjs awareness protocol is not supported by `zustand-middleware-yjs`. This means real-time user presence (e.g., who is online, current cursor position) cannot be directly managed through this middleware.
- gotcha This library is effectively in maintenance mode. The last release (v1.3.1) was in June 2023. While functional, active development and new features may not be forthcoming.
Install
-
npm install zustand-middleware-yjs -
yarn add zustand-middleware-yjs -
pnpm add zustand-middleware-yjs
Imports
- create
const create = require('zustand')import create from 'zustand'
- Y
import Y from 'yjs'
import * as Y from 'yjs'
- yjs
import { yjs } from 'zustand-middleware-yjs'import yjs from 'zustand-middleware-yjs'
Quickstart
import React from "react";
import { render } from "react-dom";
import * as Y from "yjs";
import create from "zustand";
import yjs from "zustand-middleware-yjs";
// Create a Y Doc to place our store in.
const ydoc = new Y.Doc();
// Create the Zustand store.
const useSharedStore = create(
// Wrap the store creator with the Yjs middleware.
yjs(
// Provide the Y Doc and the name of the shared type that will be used
// to hold the store.
ydoc, "shared",
// Create the store as you would normally.
(set) =>
({
count: 0,
increment: () =>
set(
(state) =>
({
count: state.count + 1,
})
),
})
)
);
// Use the shared store like you normally would any other Zustand store.
const App = () =>
{
const { count, increment } = useSharedStore((state) =>
({
count: state.count,
increment: state.increment
}));
return (
<>
<p>count: {count}</p>
<button onClick={() => increment()}>+</button>
</>
);
};
// Assuming an HTML element with id 'app-root' exists
// e.g., <div id="app-root"></div>
// render(
// <App />,
// document.getElementById("app-root")
// );
// For a runnable example without DOM, we can log the state
const unsub = useSharedStore.subscribe(state => console.log('Current state:', state));
useSharedStore.getState().increment();
useSharedStore.getState().increment();
console.log('Final state after increments:', useSharedStore.getState());
unsub();
// You would typically connect ydoc to a provider (e.g., websocket) for collaboration
// import { WebsocketProvider } from 'y-websocket';
// const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', ydoc);