{"id":17482,"library":"zustand-middleware-yjs","title":"Zustand Yjs Middleware","description":"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.","status":"maintenance","version":"1.3.1","language":"javascript","source_language":"en","source_url":"https://github.com/joebobmiles/zustand-middleware-yjs","tags":["javascript","yjs","zustand","state-management","middleware","peer-to-peer","p2p","distributed","local-first","typescript"],"install":[{"cmd":"npm install zustand-middleware-yjs","lang":"bash","label":"npm"},{"cmd":"yarn add zustand-middleware-yjs","lang":"bash","label":"yarn"},{"cmd":"pnpm add zustand-middleware-yjs","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core state management library that this package extends with Yjs capabilities.","package":"zustand","optional":false},{"reason":"CRDT framework used for collaborative state synchronization.","package":"yjs","optional":false}],"imports":[{"note":"Zustand's `create` function is a default export, typically imported with ESM syntax.","wrong":"const create = require('zustand')","symbol":"create","correct":"import create from 'zustand'"},{"note":"Yjs exports its core API under a namespace, commonly imported as `* as Y`.","wrong":"import Y from 'yjs'","symbol":"Y","correct":"import * as Y from 'yjs'"},{"note":"The middleware itself is the default export of the `zustand-middleware-yjs` package.","wrong":"import { yjs } from 'zustand-middleware-yjs'","symbol":"yjs","correct":"import yjs from 'zustand-middleware-yjs'"}],"quickstart":{"code":"import React from \"react\";\nimport { render } from \"react-dom\";\n\nimport * as Y from \"yjs\";\nimport create from \"zustand\";\nimport yjs from \"zustand-middleware-yjs\";\n\n// Create a Y Doc to place our store in.\nconst ydoc = new Y.Doc();\n\n// Create the Zustand store.\nconst useSharedStore = create(\n  // Wrap the store creator with the Yjs middleware.\n  yjs(\n    // Provide the Y Doc and the name of the shared type that will be used\n    // to hold the store.\n    ydoc, \"shared\",\n          \n    // Create the store as you would normally.\n    (set) =>\n      ({\n        count: 0,\n        increment: () =>\n          set(\n            (state) =>\n            ({\n              count: state.count + 1,\n            })\n          ),\n      })\n  )\n);\n\n// Use the shared store like you normally would any other Zustand store.\nconst App = () =>\n{\n  const { count, increment } = useSharedStore((state) =>\n    ({\n      count: state.count,\n      increment: state.increment\n    }));\n\n  return (\n    <>\n      <p>count: {count}</p>\n      <button onClick={() => increment()}>+</button>\n    </>\n  );\n};\n\n// Assuming an HTML element with id 'app-root' exists\n// e.g., <div id=\"app-root\"></div>\n// render(\n//   <App />,\n//   document.getElementById(\"app-root\")\n// );\n\n// For a runnable example without DOM, we can log the state\nconst unsub = useSharedStore.subscribe(state => console.log('Current state:', state));\nuseSharedStore.getState().increment();\nuseSharedStore.getState().increment();\nconsole.log('Final state after increments:', useSharedStore.getState());\nunsub();\n\n// You would typically connect ydoc to a provider (e.g., websocket) for collaboration\n// import { WebsocketProvider } from 'y-websocket';\n// const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', ydoc);\n","lang":"typescript","description":"This example demonstrates how to integrate `zustand-middleware-yjs` with a Zustand store, creating a collaborative counter that syncs its state via a Yjs document. It showcases store creation, middleware application, and basic state updates."},"warnings":[{"fix":"For Yjs awareness features, users must implement them separately, potentially using other Yjs-specific libraries (e.g., `y-react` mentioned by the author) or by manually managing awareness on the `Y.Doc`.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Users should be aware that future compatibility with newer Zustand or Yjs versions might require community contributions or forking if breaking changes occur in upstream dependencies.","message":"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.","severity":"gotcha","affected_versions":">=1.3.1"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"The `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.","cause":"Attempting to access or synchronize Yjs awareness state directly through the Zustand store wrapped by this middleware.","error":"Error: 'awareness' is not a property of the Y.Doc provided to zustand-middleware-yjs."},{"fix":"Ensure 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.","cause":"An incorrect `Y.Doc` instance or shared type name was passed to the `yjs` middleware, or the `Y.Doc` was not properly initialized.","error":"TypeError: Cannot read properties of undefined (reading 'observeDeep')"}],"ecosystem":"npm","meta_description":null}