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

loro-mirror-react

v1.2.1

Published

React hooks and context for Loro Mirror: type-safe CRDT-backed state with selective subscriptions.

Readme

Loro Mirror React

React integration for Loro Mirror - a state management library with Loro CRDT synchronization.

Installation

npm install loro-mirror-react loro-mirror loro-crdt
# or
yarn add loro-mirror-react loro-mirror loro-crdt
# or
pnpm add loro-mirror-react loro-mirror loro-crdt

Usage

Basic Usage with Hooks

import React, { useMemo } from "react";
import { LoroDoc } from "loro-crdt";
import { schema } from "loro-mirror";
import { useLoroStore } from "loro-mirror-react";

// Define your schema
const todoSchema = schema({
    todos: schema.LoroList(
        schema.LoroMap({
            text: schema.String({ required: true }),
            completed: schema.Boolean({ defaultValue: false }),
        }),
        // Use `$cid` (reuses Loro container id; explained below)
        (item) => item.$cid,
    ),
    filter: schema.String({ defaultValue: "all" }),
});

function TodoApp() {
    // Create a Loro document
    const doc = useMemo(() => new LoroDoc(), []);

    // Create a store
    const { state, setState } = useLoroStore({
        doc,
        schema: todoSchema,
        initialState: { todos: [], filter: "all" },
    });

    // Add a new todo (synchronous; the update is applied before return)
    const addTodo = (text: string) => {
        setState((s) => ({
            ...s,
            todos: [...s.todos, { text, completed: false }],
        }));
    };

    // Rest of your component...
}

Using Context Provider

import React, { useMemo } from "react";
import { LoroDoc } from "loro-crdt";
import { schema } from "loro-mirror";
import { createLoroContext } from "loro-mirror-react";

// Define your schema
const todoSchema = schema({
    todos: schema.LoroList(
        schema.LoroMap({
            text: schema.String({ required: true }),
            completed: schema.Boolean({ defaultValue: false }),
        }),
        (t) => t.$cid, // stable id from Loro container id
    ),
});

// Create a context
const {
    LoroProvider,
    useLoroContext,
    useLoroState,
    useLoroSelector,
    useLoroAction,
} = createLoroContext(todoSchema);

// Root component
function App() {
    const doc = useMemo(() => new LoroDoc(), []);

    return (
        <LoroProvider doc={doc} initialState={{ todos: [] }}>
            <TodoList />
            <AddTodoForm />
        </LoroProvider>
    );
}

// Todo list component
function TodoList() {
    // Subscribe only to the todos array
    const todos = useLoroSelector((state) => state.todos);

    return (
        <ul>
            {todos.map((todo) => (
                <TodoItem
                    key={todo.$cid /* stable key from Loro container id */}
                    todo={todo}
                />
            ))}
        </ul>
    );
}

// Todo item component
function TodoItem({ todo }) {
    const toggleTodo = useLoroAction((state) => {
        const todoIndex = state.todos.findIndex((t) => t.$cid === todo.$cid); // compare by `$cid`
        if (todoIndex !== -1) {
            state.todos[todoIndex].completed =
                !state.todos[todoIndex].completed;
        }
    });

    return (
        <li>
            <input
                type="checkbox"
                checked={todo.completed}
                onChange={toggleTodo}
            />
            <span>{todo.text}</span>
        </li>
    );
}

API Reference

useLoroStore

Creates and manages a Loro Mirror store.

const { state, setState, store } = useLoroStore({
  doc,
  schema,
  initialState,
  validateUpdates,
  throwOnValidationError,
  debug,
});

Notes on updates:

- `setState` from `useLoroStore` and the setter from `useLoroState` run synchronously; subsequent code can read the updated state immediately.
- `useLoroCallback` and `useLoroAction` return synchronous functions that call `setState` under the hood.

useLoroValue

Subscribes to a specific value from a Loro Mirror store.

const todos = useLoroValue(store, (state) => state.todos);

useLoroCallback

Creates a callback that updates a Loro Mirror store.

const addTodo = useLoroCallback(
    store,
    (state, text) => {
        state.todos.push({ text, completed: false }); // `$cid` is injected from Loro container id
    },
    [
        /* dependencies */
    ],
);

// Usage
addTodo("New todo");

createLoroContext

Creates a context provider and hooks for a Loro Mirror store.

const {
    LoroContext,
    LoroProvider,
    useLoroContext,
    useLoroState,
    useLoroSelector,
    useLoroAction,
} = createLoroContext(schema);

LoroProvider

Provider component for the Loro Mirror context.

<LoroProvider
    doc={loroDoc}
    initialState={initialState}
    validateUpdates={true}
    throwOnValidationError={false}
    debug={false}
>
    {children}
</LoroProvider>

useLoroContext

Hook to access the Loro Mirror store from context.

const store = useLoroContext();

useLoroState

Hook to access and update the full state.

const [state, setState] = useLoroState();

useLoroSelector

Hook to select a specific value from the state.

const todos = useLoroSelector((state) => state.todos);

useLoroAction

Hook to create an action that updates the state.

const addTodo = useLoroAction(
  (state, text) => {
    state.todos.push({ text, completed: false }); // `$cid` comes from Loro container id
  },
  [/* dependencies */]
);

### `$cid` and list keys/selectors

- `$cid` is always available on `LoroMap` state and mirrors the underlying Loro container id.
- Use `$cid` for React `key` and as the list `idSelector` for stable identity across edits and moves: `schema.LoroList(item, x => x.$cid)`.

// Usage
addTodo('New todo');

License

MIT