React hook for Yjs.
npm install react-yjsReact hook for Yjs.
The hook automatically subscribes to changes in the Yjs data-structure and re-renders the component when the data changes. In addition it returns the result of the .toJSON from the Yjs data-structure.
``bash`
npm install react-yjs
`tsx
import { useY } from "react-yjs";
export const MyComponent = ({ yArray }) => {
const names = useY(yArray)
return (
…
)
}
`
https://github.com/nikgraf/react-yjs/assets/223045/f5cbf5d7-381e-4d4b-8bce-95bbaeb8083f
`tsx
import { useY } from 'react-yjs';
import * as Y from 'yjs';
const yDoc = new Y.Doc();
const yNames = doc.getArray
export const MyComponent = () => {
const names = useY(yNames);
return (
{names.map(name =>
More Examples
$3
`tsx
const yDoc = new Y.Doc();
const yTodos = yDoc.getArray>("todos");// Any change of the todos (e.g. change checked) will trigger a re-render
const todos = useY(yTodos);
`Change Todos:
`tsx
// add a Todo
const todo = new Y.Map();
todo.set("checked", false);
todo.set("text", newTodo);
yTodos.push([todo]);// update the first Todo
yTodos.get(0).set("checked", true);
`See the working example at https://react-yjs-example.vercel.app/.
The code is available at examples/app/src/components/Todos.tsx.
$3
`tsx
const yDoc = new Y.Doc();
const yPosts = yDoc.getArray>>("posts");
const yPost = new Y.Map>();
yPosts.push([yPost]);
yPost.set("title", "Notes");
const yTags = new Y.Array();
yTags.push(["cooking", "vegetables"]);
yPost.set("tags", yTags);// Makes sure to listen only to changes of the tags of the first post
const yTagsOfFirstPost = yPosts.get(0).get("tags") as Y.Array;
const tagsOfFirstPost = useY(yTagsOfFirstPost);
`Remove a tag on the first post:
`tsx
const tags = yPosts.get(0).get("tags") as Y.Array;
tags.delete(index);
`See the working example at https://react-yjs-example.vercel.app/.
The code is available at examples/app/src/components/DeepStructure.tsx.
Architecture Decisions
The
useY hookThe goals for this project are
- trigger a re-render when the Yjs data changes
- make use of
useSyncExternalStore to avoid tearing
- allow listening to a subset of the Yjs data-structure
- allow listening to deeply nested data-structures
- simple APIThis resulted in creating a single hook that does a
observeDeep` the Yjs data-structure. This allows to expose one single hook to listen to deeply nested data-structures.Still by passing in only a specific selector of a Yjs data-structure, the hook will only listen to that specific part of the data-structure.
Yjs doesn't provide the APIs to do this on the Doc level. It would be possible to work around that, but sticking to the Yjs philosophy felt like a better option.
The Yjs types could be much better https://github.com/yjs/yjs/pull/614. Once this is release we can improve the types.
Please contribute to the project financially - especially if your company relies
on it. https://github.com/sponsors/nikgraf
The project is MIT licensed.