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

@signal-kernel/react

v0.5.4

Published

Thin React adapter for subscribing to signal-kernel graph values and async resources without making React own the graph.

Downloads

76

Readme


@signal-kernel/react is a thin rendering adapter for React applications that need to observe an existing signal-kernel graph. It subscribes and renders snapshots without moving graph ownership, business logic, or async lifecycle into React.

Core signals, computed values, effects, batching, and invalidation semantics remain owned by @signal-kernel/core.

Install

pnpm add @signal-kernel/react @signal-kernel/core @signal-kernel/async-runtime

react and react-dom are peer dependencies and are expected to already exist in the React application.

Core Bridge

import { computed, signal } from "@signal-kernel/core";
import { useKernelValue } from "@signal-kernel/react";

const count = signal(0);
const doubled = computed(() => count.get() * 2);

export function Counter() {
  const value = useKernelValue(count);
  const label = useKernelValue(doubled);

  return (
    <button onClick={() => count.set(value + 1)}>
      Count: {value}, doubled: {label}
    </button>
  );
}

useKernelValue() is the preferred bridge for a single readable signal-kernel graph value. It accepts values that expose get() and peek(), including signals and computed values.

useSignalValue() and useComputedValue() remain available for compatibility and for call sites that want a signal-specific or computed-specific readability hint.

Reading Multiple Values

Use useReactive() to read an existing reactive scope from React. Derived graph logic should still live in computed() or other runtime primitives.

import { computed, signal } from "@signal-kernel/core";
import { useReactive } from "@signal-kernel/react";

const count = signal(1);
const doubled = computed(() => count.get() * 2);
const status = signal("idle");

function Dashboard() {
  const state = useReactive(() => ({
    count: count.get(),
    doubled: doubled.get(),
    status: status.get(),
  }));

  return <div>{state.doubled}</div>;
}

Snapshot Strategy

useReactive() bridges a reactive read function into React through useSyncExternalStore.

The read function is used to collect graph dependencies inside the signal-kernel runtime, while React only consumes the returned snapshot. This keeps React rendering as a snapshot consumer instead of making React own the graph.

For low-level readable sources, useReadableValue() supports different read strategies:

  • snapshot: "peek" reads the current value without forcing reactive evaluation.
  • snapshot: "get" reads through the reactive getter and can initialize lazy computed values.
  • track: "get" is used to collect dependencies for external-store updates.

Most users should prefer the higher-level hooks:

  • useKernelValue() for single readable graph values
  • useSignalValue() for signals
  • useComputedValue() for computed values
  • useResource() for async resources
  • useStreamResource() for streaming async resources

useKernelValue() and useComputedValue() intentionally read through get() so lazy computed values can be initialized correctly when first observed by React. useSignalValue() remains available when a call site specifically wants the signal-oriented peek() snapshot strategy.

useReadableValue() is exported as an advanced bridge for adapter authors and unusual readable-like sources. Prefer the dedicated hooks in application code. Choosing the wrong snapshot or track strategy can miss graph dependencies, force lazy values too early, or make React render from a different read path than the one used for subscription.

Async Bridge

import { signal } from "@signal-kernel/core";
import { createResource } from "@signal-kernel/async-runtime";
import { useResource } from "@signal-kernel/react";

const userId = signal("1");

const userResource = createResource({
  input: userId.get,
  run: async (id, ctx) => {
    const response = await fetch(`/api/users/${id}`, {
      signal: ctx.signal,
    });

    return response.json() as Promise<{ name: string }>;
  },
});

function UserView() {
  const [user, meta] = useResource(userResource);

  if (meta.status() === "pending") return <div>Loading...</div>;
  if (meta.status() === "error") return <div>{String(meta.error())}</div>;

  return <div>{user?.name}</div>;
}

The async hooks consume resource tuples created by @signal-kernel/async-runtime. They observe both resource values and metadata getters, including status and error states, so metadata-only transitions can re-render React.

useStreamResource() also observes stream metadata such as stable values, allowing React to update when streaming state changes even if the visible value has not changed.

useResource() and useStreamResource() return the current value snapshot together with the original resource metadata object:

const [value, meta] = useStreamResource(resource);
const status = meta.status();

The returned value is the value captured by the adapter snapshot for this render. The returned meta is still the live async-runtime metadata object. The hook subscribes to metadata reads internally so meta.status(), meta.error(), and stream meta.stableValue() changes can trigger React updates, but ownership of those transitions remains in @signal-kernel/async-runtime.

Unmounting a consumer only removes the React subscription. It does not call meta.cancel() or meta.dispose(), because the resource may be shared by other consumers. Application code may explicitly connect meta.dispose() to a lifecycle that truly owns the resource.

When a manual resource exposes runnable metadata, useResource() preserves that metadata type, so meta.run(input) remains available after passing through the React adapter.

When a component needs value and metadata to be consumed as one named render snapshot, wrap the tuple locally in a small hook that reads the value and metadata inside one useReactive() call. Keep that hook in the adapter or application boundary; do not move rendering concerns into the graph or async runtime.

These hooks do not add caching, retry, cancellation, or Suspense policy. Those behaviors remain owned by @signal-kernel/async-runtime.

The low-level bridge options are exported for adapter-level use, but application code should normally use the dedicated hooks instead of configuring read strategies manually.

Boundary

Use React event handlers to write to graph values. Use computed() for graph derivation. Use React useEffect() for imperative component lifecycle work such as DOM APIs, browser subscriptions, focus management, and third-party widgets.