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 🙏

© 2025 – Pkg Stats / Ryan Hefner

valtio-y

v1.1.2

Published

Collaborative Valtio state

Downloads

260

Readme

valtio-y

npm version bundle size

Two-way sync between Valtio proxies and Yjs CRDTs. Build collaborative apps with automatic conflict resolution and offline support—just mutate objects naturally.

state.todos.push({ text: "Buy milk", done: false });
state.users[0].name = "Alice";
// Automatically syncs across all connected users

Live Examples

Open any demo in multiple browser tabs and watch them sync in real-time:

🎮 Minecraft Clone - Simple showcase inspired by Minecraft. Lets multiple users place and remove blocks in real time using Three.js and valtio-y.

🎨 Whiteboard - Collaborative drawing with shapes, colors, and real-time cursors. Google Docs for drawing.

📝 Sticky Notes - Production-ready app running on Cloudflare Workers (this is real infrastructure, not a demo server).

Todos App - Classic collaborative todo list. Real-time updates, no refresh needed.

🧪 Simple Demo – Best for understanding the basic sync patterns (objects, arrays, primitives); other demos above are more production-focused.

Quick Start

Create a synchronized proxy and mutate it like any normal object. Changes automatically sync across clients.

import * as Y from "yjs";
import { createYjsProxy } from "valtio-y";

// Create a Yjs document
const ydoc = new Y.Doc();

// Create a synchronized proxy
const { proxy: state } = createYjsProxy(ydoc, {
  getRoot: (doc) => doc.getMap("root"),
});

// 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.

Installation

# npm
npm install valtio-y valtio yjs

# pnpm
pnpm add valtio-y valtio yjs

# bun
bun add valtio-y valtio yjs

React Integration

Use Valtio's useSnapshot hook to automatically re-render components when data changes:

import { useSnapshot } from "valtio/react";

function TodoList() {
  const snap = useSnapshot(state);

  return (
    <ul>
      {snap.todos.map((todo, i) => (
        <li key={i}>
          <input
            type="checkbox"
            checked={todo.done}
            onChange={() => (state.todos[i].done = !state.todos[i].done)}
          />
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

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.

Note for text inputs: When using controlled text inputs (like <input> or <textarea>), add { sync: true } to prevent cursor jumping:

const snap = useSnapshot(state, { sync: true });
<input value={snap.text} onChange={(e) => (state.text = e.target.value)} />;

This forces synchronous updates instead of Valtio's default async batching. See Valtio issue #270 for details.

Collaboration Setup

Connect any Yjs provider to sync across clients:

import { WebsocketProvider } from "y-websocket";

const provider = new WebsocketProvider(
  "ws://localhost:1234",
  "room-name",
  ydoc
);
// That's it—state syncs automatically

Works with any provider: y-websocket, y-partyserver (great for Cloudflare), y-webrtc, y-indexeddb, etc.

Common Operations

Initializing State

When using network providers, initialize after first sync:

const { proxy: state, bootstrap } = createYjsProxy(ydoc, {
  getRoot: (doc) => doc.getMap("state"),
});

provider.once("synced", () => {
  bootstrap({
    todos: [],
    settings: { theme: "light" },
  });
  // Only writes if the document is empty
});

Arrays

state.items.push(newItem);
state.items[0] = updatedItem;
state.items.splice(1, 2, replacement1, replacement2);
const [item] = state.items.splice(2, 1);
state.items.splice(0, 0, item); // Move item

Objects

state.user.name = "Alice";
delete state.user.temporaryFlag;
state.data.deeply.nested.value = 42;

Undo/Redo

const {
  proxy: state,
  undo,
  redo,
} = createYjsProxy(ydoc, {
  getRoot: (doc) => doc.getMap("state"),
  undoManager: true, // Enable with defaults
});

state.count = 1;
undo(); // state.count -> undefined
redo(); // state.count -> 1

See API documentation for configuration options.

Features

  • Zero API overhead - Just mutate objects like normal JavaScript
  • Fine-grained updates - Components re-render only when their data changes
  • Offline-first - Changes merge automatically when reconnected
  • TypeScript - Full type safety and inference
  • Production-ready - Comprehensive tests and benchmarks
  • Framework-agnostic - Works with React, Vue, Svelte, Solid, and vanilla JS

Why valtio-y?

Stop writing sync logic. Just mutate objects.

  • Valtio gives you reactive state with zero boilerplate
  • Yjs gives you conflict-free sync and offline support
  • valtio-y connects them - you get both, write neither

No reducers, no actions, no manual sync code. Just: state.count++

Limitations

  • Don't use undefined (use null or delete the property)
  • Don't store functions or class instances (not serializable)
  • Use array.splice() instead of array.length = N

For text editors, use native Yjs integrations: Lexical, TipTap, or ProseMirror.

API Reference

createYjsProxy(doc, options)

const { proxy, bootstrap } = createYjsProxy(ydoc, {
  getRoot: (doc: Y.Doc) => Y.Map<any>,
});

Returns:

  • proxy - Valtio proxy for state mutations
  • bootstrap(data) - Initialize state (no-op if doc not empty)

Resources