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

@lazily-hub/lazily-react

v0.3.0

Published

React/Preact bindings for @lazily-hub/lazily-js: drive React state from lazily reactive Source/Computed handles via useSyncExternalStore. Glitch-free, equality-guarded re-renders.

Readme

@lazily-hub/lazily-react

React / Preact bindings for @lazily-hub/lazily-js — drive React state from the lazily Cell kernel (Source / Computed) via useSyncExternalStore. Glitch-free, equality-guarded re-renders.

The whole binding is a thin adapter: a lazily effect reads the handle (registering the dependency edge) and calls React's re-render callback on invalidation; useSyncExternalStore reads the cached value. lazily's pull-based glitch-free computeds and its deep-equality guards carry straight through to React.

Install

npm install @lazily-hub/lazily-react @lazily-hub/lazily-js react

Preact instead of React: alias react → preact/compat (the standard convention). useSyncExternalStore is exported by both react (≥18) and preact/compat (≥10.16), so no code changes are needed.

// vite.config.js / bundler alias
{ resolve: { alias: { react: "preact/compat" } } }

Usage

import { createContext } from "@lazily-hub/lazily-js/reactive";
import { LazilyProvider, useSource, useComputed } from "@lazily-hub/lazily-react";

const ctx = createContext();

function Counter() {
  const [count, setCount] = useSource(0);
  const doubled = useComputed(() => count * 2, [count]);
  return (
    <button onClick={() => setCount((c) => c + 1)}>
      {count} (doubled: {doubled})
    </button>
  );
}

export function App() {
  return <LazilyProvider context={ctx}><Counter /></LazilyProvider>;
}

Hooks

| hook | Cell kernel construction | lazy? | equality guard? | |-------------------|--------------------------|-------|-----------------| | useSource(initial)| ctx.source (Source) | src | yes (on write) | | useComputed(fn, deps) | ctx.computed (Computed) | yes | yes | | useLazily(handle)| any (read-only) | — | from handle | | useLatestDurableSnapshot(projection) | latest-durable snapshot reader | yes | yes | | useLatestDurableEntry(projection, key) | one latest-durable key reader | yes | yes | | useLatestDurableGeneration(projection) | actor generation fence reader | yes | yes |

There is intentionally no useSlot and no useSignal:

  • useSlot is deleted (Cell kernel, #lzcellkernel — design §9.4 step 6, zero call sites). Under the Cell kernel v2 every computed is guarded — there is no unguarded derived construction (slot survives in lazily-js only as a deprecated alias of the guarded computed). Equal recomputes do not propagate, so the one derived hook is the guarded useComputed.

  • useSignal never existed. The eager construction is now ctx.computed(f).eager(), and a React binding gains nothing from making it eager — React only renders on invalidation, and getSnapshot reads the (lazily-recomputed-on-read) computed, so it always sees the fresh value with no stale-frame risk. useLazily reads externally-created Source and Computed handles without creating an eager wrapper.

  • useSource — component-local mutable source (a Source via ctx.source), returns [value, setValue] like useState. setValue accepts a value or (prev) => next. The source is disposed on real unmount.

  • useComputed — the default (and only) derived hook: a guarded Computed. Equal recomputes are suppressed at the lazily level (the subscribe effect never runs), so React never re-renders on a no-op recompute.

The equality guard at the React level

useSyncExternalStore uses Object.is on the snapshot. With a primitive recompute that comes back equal, useComputed skips the re-render for free. The guard earns its keep when the compute returns a fresh object each invalidation:

  • useComputed(() => ({ n: a % 2 })): lazily's deep-equal guard suppresses propagation → no re-render, even though the reference changed.

(See test/hooks.test.js.)

Sharing handles across components

useSource creates a component-local Source. To share state, create the source externally and read it with useLazily; write it via ctx.set:

const shared = ctx.source(0);
// in any component: const v = useLazily(shared);
// anywhere: ctx.set(shared, v + 1);

Latest durable projection

The latest-durable-projection entry point adapts lazily-js's LatestDurableProjection to React. The snapshot, keyed-entry, and generation hooks subscribe to the projection's memoized readers, so a component observes the latest desired value, in-flight claim, monotone durable frontier, and reconnect generation without owning the state machine. Commands remain on the projection itself and therefore preserve its per-key single-flight ordering.

const projection = new LatestDurableProjection(ctx, 1);

function SaveStatus({ documentId }) {
  const state = useLatestDurableEntry(projection, documentId);
  return <span>{state?.inflight ? "saving" : state?.desired ? "pending" : "saved"}</span>;
}

The integration suite replays lazily-spec v0.38.0's canonical egress/latest_durable_projection.json fixture through a mounted React subscriber. The state machine corresponds to LazilyFormal.LatestDurableProjectionCore in lazily-formal v0.38.1.

How it works

React component
  └─ useSyncExternalStore(subscribe, getSnapshot)
       ├─ subscribe  = lazily effect that reads the handle (registers edge) + onChange
└─ getSnapshot = ctx.get(handle)
  • lazily effects flush synchronously before set/batch returns, matching the notify-then-read contract useSyncExternalStore expects.
  • Snapshot stability (required to avoid React's "getSnapshot should be cached" loop) comes for free: lazily caches node values and only changes the reference on a real change.
  • The bridge skips the initial forced effect run's onChange (subscribe should notify only future changes); the first run still registers the dependency edge.

The framework-agnostic adapter lives in src/bridge.js (readHandle, createLazilySubscription) and is unit-tested without React in test/bridge.test.js.

Node lifetime

useSource disposes its Source and useComputed disposes its Computed on real unmount and on deps-change via each handle's canonical dispose() method. Disposal is strict-mode-safe: it is deferred one microtask and cancelled if React 18 dev's simulated remount (setup → cleanup → setup) re-subscribes the same handle, so the dev double-invoke never frees a handle that the second setup still uses. useLazily is read-only and does NOT dispose its externally-owned handle (the caller manages its lifetime).

Develop

make check   # build (node --check) + node:test
npm test     # node --test test/*.test.js

The lazily family

lazily is one reactive kernel — Source / Computed / Effect, keyed collections, state charts, CRDTs, and a distributed plane — implemented natively in each language and held to a single cross-language contract:

  • lazily-spec — the wire protocol, the generated feature matrix, and the conformance corpus every binding replays.
  • lazily-formal — the Lean 4 formal model the bindings share.

lazily-react is not one of the language bindings. It is a thin React / Preact adapter layered over the JavaScript binding: @lazily-hub/lazily-react owns only the hooks (useSource, useComputed, useLazily, LazilyProvider) and the useSyncExternalStore bridge, while every reactive primitive, CRDT, and wire type comes from @lazily-hub/lazily-js. Conformance against lazily-spec is lazily-js's job, not this package's.

| repo | language | |---|---| | lazily-rs | Rust — the reference implementation | | lazily-py | Python | | lazily-go | Go | | lazily-kt | Kotlin / JVM | | lazily-js | JavaScript / TypeScript — the package this one layers over | | lazily-cs | C# / .NET | | lazily-cpp | C++ | | lazily-zig | Zig | | lazily-dart | Dart / Flutter | | lazily-react | React / Preact over lazily-js — you are here |

Per-binding feature parity is tracked in the coverage.json-generated matrix in lazily-spec; read it there rather than any hand copy.

License

Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.