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

react-cosmic

v0.1.1

Published

CRDT-based offline-first React state management

Downloads

211

Readme

React Cosmic

State that persists. Across tabs. Across crashes. No server required.

Built on CRDTs so your users can't lose their work, even when they try.

What is this?

You know how form data disappears when you refresh the page? Or how opening the same app in two tabs creates a mess? This fixes that.

React Cosmic wraps Yjs and gives you hooks that work like useState but actually remember things. Your state syncs across browser tabs automatically and persists to IndexedDB without you doing anything.

Install

bun add react-cosmic  # or npm/pnpm/yarn

Quick start

Wrap your app:

import { OrbitProvider } from 'react-cosmic';

function App() {
  return (
    <OrbitProvider storeId="my-app">
      <YourStuff />
    </OrbitProvider>
  );
}

Use the hooks:

import {
  useOrbit,
  useOrbitText,
  useOrbitObject,
  useOrbitStatus,
  useOrbitAwareness,
  useSetLocalAwareness
} from 'react-cosmic';

function Form() {
  const [email, setEmail] = useOrbit('email', '');
  const [bio, setBio] = useOrbitText('bio', '');
  const [profile, updateProfile] = useOrbitObject('profile', {
    name: '',
    age: 0
  });

  return (
    <form>
      <input
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <textarea
        value={bio}
        onChange={(e) => setBio(e.target.value)}
      />
      <input
        value={profile.name}
        onChange={(e) => updateProfile({ name: e.target.value })}
      />
    </form>
  );
}

That's it. Now open two tabs and watch them sync. Close the browser and reopen it. Your data is still there.

API

<OrbitProvider>

Put this around your app or component tree.

<OrbitProvider
  storeId="unique-id"           // required - same ID = shared state
  enableStorage={true}          // optional - persist to IndexedDB (default: true)
  enableTabSync={true}          // optional - sync across tabs (default: true)
  persistDebounceMs={300}       // optional - debounce saves (default: 300)
  websocketUrl="ws://localhost:1234"  // optional - sync across devices/users
  websocketOptions={{           // optional - WebSocket config
    retryDelay: 3000,           // reconnect delay in ms
    maxRetries: Infinity,       // retry attempts
    protocols: ["my-protocol"]  // WebSocket protocols
  }}
>

useOrbit(key, initialValue)

Like useState but it remembers. Use for primitives and simple values.

const [count, setCount] = useOrbit('count', 0);
const [name, setName] = useOrbit('name', '');
const [enabled, setEnabled] = useOrbit('enabled', false);

useOrbitText(key, initialValue?)

For text fields. Uses Yjs Text internally for proper collaborative editing.

const [content, setContent] = useOrbitText('doc-content', '');

useOrbitObject(key, initialValue)

For nested objects. Lets you update individual properties without replacing the whole thing.

const [user, updateUser] = useOrbitObject('user', {
  name: '',
  email: '',
  age: 0
});

updateUser({ age: 25 });  // only updates age, keeps name and email

useOrbitStatus()

Track WebSocket connection status. Returns 'connected', 'connecting', or 'disconnected'.

const status = useOrbitStatus();

return <div>Status: {status}</div>;

Only useful when websocketUrl is configured. Returns 'disconnected' if WebSockets aren't enabled.

useOrbitAwareness<T>()

Read presence data for all connected clients. Returns a Map<number, T> where the key is the client ID and the value is their awareness state.

interface UserPresence {
  name: string;
  color: string;
  cursor?: number;
}

const users = useOrbitAwareness<UserPresence>();

return (
  <div>
    {Array.from(users.values()).map(user => (
      <div key={user.name}>{user.name}</div>
    ))}
  </div>
);

Only works when websocketUrl is configured.

useSetLocalAwareness<T>(state)

Broadcast your presence state to all connected clients. Automatically cleans up on unmount.

interface UserPresence {
  name: string;
  color: string;
}

function MyComponent() {
  const [profile] = useOrbit<UserPresence>('profile', {
    name: 'Alice',
    color: '#ff0000'
  });

  useSetLocalAwareness(profile);

  return <div>Your name: {profile.name}</div>;
}

Only works when websocketUrl is configured.

Try the demos

Basic form demo (local persistence and tab sync):

git clone https://github.com/JR-G/react-cosmic
cd react-cosmic
bun install
bun run demo

Open multiple tabs to see cross-tab sync in action.

Collaboration demo (real-time multi-user sync with presence):

Terminal 1 - start the WebSocket server:

bun run demo:server

Terminal 2 - start the demo:

bun run demo:collab

Open multiple browsers to see real-time collaboration with presence indicators and remote cursors.

When to use this

Good for:

  • Forms that shouldn't lose data
  • Draft content (emails, posts, documents)
  • User preferences
  • Any UI state you want to survive refreshes
  • Multi-tab scenarios
  • Real-time collaboration (with WebSocket server)
  • Cross-device state sync

Not for:

  • Replacing your backend (it's a sync layer, not a database)
  • Massive datasets (it's in-browser storage)

Server sync (CRDT-powered collaboration)

Want to sync across devices or add real-time multiplayer? Run a WebSocket server. The same CRDT that syncs your tabs now syncs across devices and users, with automatic conflict resolution.

The simplest option is using bunx:

bunx y-websocket-server --port 1234

Or install globally:

bun install -g y-websocket
y-websocket-server --port 1234

Then connect your app:

<OrbitProvider
  storeId="my-app"
  websocketUrl="ws://localhost:1234"
>
  <YourStuff />
</OrbitProvider>

Now users on different devices see each other's changes in real-time. The CRDT handles all conflict resolution automatically - whether it's two tabs, two users, or twenty.

For production, you'll want auth, persistence, and proper scaling. Check out y-websocket docs or hosted Yjs providers.

License

MIT