npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

immer-yjs

v1.2.0

Published

Combine immer & y.js

Readme

immer-yjs

npm size

Combine immer & y.js

What is this

immer is a library for easy immutable data manipulation using plain json structure. y.js is a CRDT library with mutation-based API. immer-yjs allows manipulating y.js data types with the api provided by immer.

  • Two-way binding between y.js and plain (nested) json object/array.
  • Efficient snapshot update with structural sharing, same as immer.
  • Updates to y.js are explicitly batched in transaction, you control the transaction boundary.
  • Always opt-in, non-intrusive by nature (the snapshot is just a plain object after all).
  • The snapshot shape & y.js binding aims to be fully customizable.
  • Typescript all the way (pure js is also supported).
  • Code is simple and small, no magic hidden behind, no vendor-locking.

Do:

// any operation supported by immer
update((state) => {
    state.nested[0].key = {
        id: 123,
        p1: 'a',
        p2: ['a', 'b', 'c'],
    }
})

Instead of:

Y.transact(state.doc, () => {
    const val = new Y.Map()
    val.set('id', 123)
    val.set('p1', 'a')

    const arr = new Y.Array()
    arr.push(['a', 'b', 'c'])
    val.set('p2', arr)

    state.get('nested').get(0).set('key', val)
})

Installation

yarn add immer-yjs immer yjs

Documentation

  1. import { bind } from 'immer-yjs'.
  2. Create a binder: const binder = bind(doc.getMap("state")).
  3. Add subscription to the snapshot: binder.subscribe(listener).
    1. Mutations in y.js data types will trigger snapshot subscriptions.
    2. Calling update(...) (similar to produce(...) in immer) will update their corresponding y.js types and also trigger snapshot subscriptions.
  4. Call binder.get() to get the latest snapshot.
  5. (Optionally) call binder.unbind() to release the observer.

Y.Map binds to plain object {}, Y.Array binds to plain array [], and any level of nested Y.Map/Y.Array binds to nested plain json object/array respectively.

Y.XmlElement & Y.Text have no equivalent to json data types, so they are not supported by default. If you want to use them, please use the y.js top-level type (e.g. doc.getText("xxx")) directly, or see Customize binding & schema section below.

With Vanilla Javascript/Typescript

🚀🚀🚀 Please see the test for detailed usage. 🚀🚀🚀

Customize binding & schema

Use the applyPatch option to customize it. Check the discussion for detailed background. This section will likely be removed since it is not functioning properly. A new impl may be needed

Integration with React

By leveraging useSyncExternalStoreWithSelector.

import { bind } from 'immer-yjs'

// define state shape (not necessarily in js)
interface State {
    // any nested plain json data type
    nested: { count: number }[]
}

const doc = new Y.Doc()

// optionally set initial data to doc.getMap('data')

// define store
const binder = bind<State>(doc.getMap('data'))

// define a helper hook
function useImmerYjs<Selection>(selector: (state: State) => Selection) {
    const selection = useSyncExternalStoreWithSelector(binder.subscribe, binder.get, binder.get, selector)

    return [selection, binder.update]
}

// optionally set initial data
binder.update((state) => {
    state.nested = [{ count: 0 }]
})

// use in component
function Component() {
    const [count, update] = useImmerYjs((s) => s.nested[0].count)

    const handleClick = () => {
        update((s) => {
            // any operation supported by immer
            s.nested[0].count++
        })
    }

    // will only rerender when 'count' changed
    return <button onClick={handleClick}>{count}</button>
}

// when done
binder.unbind()

Integration with other frameworks

Please submit with sample code by PR, helps needed.

Demos

Data will sync between multiple browser tabs automatically.

Changelog

Changelog

Similar projects

valtio-yjs