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

@lazarv/rsc

v0.0.0-experimental-04f2240-20260306-7fcde375

Published

Bundler-agnostic React Server Components serialization and deserialization

Readme

@lazarv/rsc

A bundler-agnostic, environment-agnostic React Server Components (RSC) serialization and deserialization library built on React's Flight protocol. Not a framework — a universal data transport layer.

This package provides a standalone implementation of the Flight protocol without any direct dependency on the react package and without bundler-specific mechanisms like Webpack's __webpack_require__. It is part of the @lazarv/react-server project.

Part of the @lazarv/react-server projectWebsite · GitHub


Why

React's official react-server-dom-webpack package is tightly coupled to Webpack manifests and Node.js APIs. @lazarv/rsc removes both constraints:

  • Bundler-agnostic — no Webpack plugin, no Vite plugin, no bundler manifests. Consumers wire up their own moduleResolver / moduleLoader interfaces.
  • Environment-agnostic — built on Web Platform APIs (ReadableStream, WritableStream, TextEncoder, FormData, Blob, URL, …). The same code runs in Node.js, Deno, Bun, Cloudflare Workers, the browser, or any runtime that supports the Web Platform.
  • No direct React imports — uses Symbol.for() to access React internals, so it works with any compatible React version.
  • Full Flight protocol parity — Elements, Promises, Map, Set, Date, BigInt, RegExp, Symbol, URL, URLSearchParams, FormData, TypedArrays, ArrayBuffer, DataView, Blob, ReadableStream, async iterables, client/server references, Suspense, Fragment, lazy, memo, forwardRef, context, Activity, ViewTransition, and more.

How @lazarv/react-server uses it

@lazarv/react-server is a Vite-based React Server Components framework. It currently uses @lazarv/rsc for:

  • Logger proxy — serializing structured log data across environment boundaries using the Flight protocol.
  • Caching / cache providers — saving and restoring UI or data snapshots in any storage backend via RSC serialization.

With planned expansion to full cross-environment usage (worker threads, edge runtimes, cross-process communication) leveraging the environment-agnostic design of this package.

By extracting the Flight protocol into a standalone package, any tool or framework can adopt RSC serialization without buying into a specific bundler or runtime.


Use Cases

| Direction | Example | |---|---| | Server → Client | Streaming serialized React trees or structured data | | Client → Server | Sending action arguments, form data, console logs in RSC format | | Worker threads | Passing serialized React trees between threads | | Cache providers | Saving/restoring UI or data snapshots in any storage backend | | Cross-process | Piping RSC payloads between server processes | | Any ↔ Any | Browser ↔ Server ↔ Worker ↔ Edge ↔ Cache |


Installation

npm install @lazarv/rsc
# or
pnpm add @lazarv/rsc
# or
yarn add @lazarv/rsc

Peer dependency: react >=19.0.0 (or >=0.0.0-experimental).


Entry Points

Two universal entry points — the same code runs everywhere that supports Web Platform APIs:

| Entry | Purpose | |---|---| | @lazarv/rsc/server | Serialization — render, register references, decode replies | | @lazarv/rsc/client | Deserialization — consume streams, encode replies, call actions |

There are no platform-specific sub-entries. No /server.node, /server.edge, /client.browser, etc.


Usage

Server-side (Serialization)

import {
  renderToReadableStream,
  registerServerReference,
  registerClientReference,
  createClientModuleProxy,
  createTemporaryReferenceSet,
  decodeReply,
  decodeAction,
  decodeFormState,
  decodeReplyFromAsyncIterable,
} from "@lazarv/rsc/server";

Module Resolver

Provide a module resolver that tells the serializer how to resolve "use client" / "use server" references:

const stream = renderToReadableStream(element, {
  moduleResolver: {
    resolveClientReference(reference) {
      return {
        id: reference.$$id,
        name: reference.$$name,
        chunks: [/* chunk IDs to preload */],
      };
    },
    resolveServerReference(reference) {
      return {
        id: reference.$$id,
        name: reference.$$name,
      };
    },
  },
  onError(error) {
    console.error("RSC Error:", error);
    return error.digest; // returned as error.digest on the client
  },
});

Registering References

// Register a client component
const ClientComponent = registerClientReference(
  {},                    // proxy object
  "./ClientComponent",   // module ID
  "default"              // export name
);

// Register a server action
async function submitForm(formData) {
  "use server";
}
registerServerReference(submitForm, "action:submitForm", "submitForm");

// Create a full module proxy (all named exports become client references)
const clientModule = createClientModuleProxy("./MyClientModule");

Decoding Client Replies

// Decode a reply from the client (FormData or string body)
const args = await decodeReply(body, {
  moduleLoader: {
    loadServerAction: (id) => actionRegistry.get(id),
  },
});

// Decode from a streaming async iterable
const args = await decodeReplyFromAsyncIterable(requestBodyStream, {
  moduleLoader: {
    loadServerAction: (id) => actionRegistry.get(id),
  },
});

// Decode a form action (returns the action function)
const action = await decodeAction(formData, { moduleLoader });

// Decode form state for progressive enhancement
const state = await decodeFormState(actionResult, formData);

Client-side (Deserialization)

import {
  createFromReadableStream,
  createFromFetch,
  encodeReply,
  createServerReference,
  createTemporaryReferenceSet,
} from "@lazarv/rsc/client";

Module Loader

Provide a module loader that tells the deserializer how to load client modules:

const moduleLoader = {
  requireModule(metadata) {
    // Synchronously return the module export
    return moduleCache.get(metadata.id)?.[metadata.name];
  },
  preloadModule(metadata) {
    // Optional: preload module chunks ahead of time
    return import(metadata.id);
  },
};

Consuming Flight Streams

// From a ReadableStream — returns a synchronous thenable
// compatible with React's use() protocol
const result = createFromReadableStream(stream, {
  moduleLoader,
  callServer: async (id, args) => {
    const response = await fetch("/action", {
      method: "POST",
      body: await encodeReply(args),
    });
    return createFromFetch(response, { moduleLoader, callServer });
  },
});

// result.status === "pending" | "fulfilled" | "rejected"
// result.value is available synchronously once fulfilled

// From a fetch response
const result = createFromFetch(fetch("/rsc"), { moduleLoader, callServer });

Server Action References

// Create a callable server action proxy
const myAction = createServerReference("action:myAction", callServer);

// Call it — args are encoded and sent via callServer
await myAction(arg1, arg2);

// .bind() works for partial application
const boundAction = myAction.bind(null, boundArg);
await boundAction(remainingArg);

Encoding Replies

// Encode arguments for a server action call
// Returns string or FormData depending on content
const encoded = await encodeReply([arg1, arg2]);

Temporary References

Temporary references allow non-serializable values (functions, React elements, class instances, local symbols) to survive a round-trip:

// Client-side: create a Map-based temp ref set
import { createTemporaryReferenceSet } from "@lazarv/rsc/client";
const tempRefs = createTemporaryReferenceSet();

// Encode with temp refs — non-serializable values are stored in the Map
const encoded = await encodeReply(args, { temporaryReferences: tempRefs });

// Later, recover values when consuming a response
const result = createFromReadableStream(responseStream, {
  moduleLoader,
  temporaryReferences: tempRefs,
});
// Server-side: create a WeakMap-based temp ref set
import { createTemporaryReferenceSet } from "@lazarv/rsc/server";
const tempRefs = createTemporaryReferenceSet();

// Decode with temp refs
const args = await decodeReply(body, { temporaryReferences: tempRefs });

// Render with the same temp refs — proxies resolve back to $T references
const stream = renderToReadableStream(element, {
  temporaryReferences: tempRefs,
});

API Reference

Server API (@lazarv/rsc/server)

| Export | Description | |---|---| | renderToReadableStream(model, options?) | Serialize a React element tree to a Flight ReadableStream | | decodeReply(body, options?) | Decode a FormData or string reply from the client | | decodeReplyFromAsyncIterable(iterable, options?) | Decode a reply from a streaming AsyncIterable<Uint8Array> | | decodeAction(body, options?) | Decode a server action invocation from FormData | | decodeFormState(result, body) | Decode form state for progressive enhancement | | registerServerReference(fn, id, name) | Register a function as a server reference ("use server") | | registerClientReference(proxy, id, name) | Register an object as a client reference ("use client") | | createClientModuleProxy(moduleId) | Create a Proxy where every property access returns a client reference | | createTemporaryReferenceSet() | Create a WeakMap for temporary reference tracking | | prerender(model, options?) | Prerender a model to a static prelude ReadableStream (waits for all async work) |

Client API (@lazarv/rsc/client)

| Export | Description | |---|---| | createFromReadableStream(stream, options?) | Deserialize a Flight stream into a React element tree (synchronous thenable) | | createFromFetch(responsePromise, options?) | Deserialize a fetch() response into a React element tree | | encodeReply(value, options?) | Encode a value for sending to the server (string or FormData) | | createServerReference(id, callServer) | Create a callable proxy for a server action | | createTemporaryReferenceSet() | Create a Map for temporary reference tracking |


Types

Full type definitions are in types.d.ts. Key interfaces:

interface ModuleResolver {
  resolveClientReference?(reference: unknown): ClientReferenceMetadata | null;
  resolveServerReference?(reference: unknown): ServerReferenceMetadata | null;
}

interface ModuleLoader {
  preloadModule?(metadata: ClientReferenceMetadata): Promise<void> | void;
  requireModule(metadata: ClientReferenceMetadata): unknown;
  loadServerAction?(id: string): Promise<Function> | Function;
}

interface ClientReferenceMetadata {
  id: string;
  name: string;
  chunks?: string[];
}

interface ServerReferenceMetadata {
  id: string;
  bound?: boolean;
}

interface RenderToReadableStreamOptions {
  moduleResolver?: ModuleResolver;
  onError?: (error: unknown) => string | void;
  identifierPrefix?: string;
  temporaryReferences?: Map<string, unknown>;
  environmentName?: string;
  filterStackFrame?: (sourceURL: string, functionName: string) => boolean;
  signal?: AbortSignal;
}

interface CreateFromReadableStreamOptions {
  moduleLoader?: ModuleLoader;
  callServer?: (id: string, args: unknown[]) => Promise<unknown>;
  temporaryReferences?: Map<string, unknown>;
  typeRegistry?: Record<string, new (buffer: ArrayBuffer) => ArrayBufferView>;
}

Serialization Coverage

All types supported by React's Flight protocol are implemented:

| Category | Types | |---|---| | Primitives | string, number, boolean, null, undefined, BigInt, Symbol (global) | | Objects | Date, RegExp, URL, URLSearchParams, Map, Set, Error | | Binary | ArrayBuffer, Int8Array, Uint8Array, Float32Array, DataView, Blob, … | | Streams | ReadableStream, AsyncIterable | | Form data | FormData | | React | Elements, Fragments, Suspense, Lazy, Memo, ForwardRef, Context, Activity, ViewTransition | | RSC | Client references ("use client"), Server references ("use server"), bound actions (.bind()) | | Special | Promises, Thenables, Temporary references, Error digest propagation |


Design Decisions

Web standards only

This library targets the Web Platform API surface (ReadableStream, WritableStream, TextEncoder, FormData, Blob, URL, …). No Node.js-specific primitives — no stream.Readable, AsyncLocalStorage, or Buffer. A single entry point per side runs in any environment.

Abstract module loader — no bundler coupling

Only abstract moduleResolver / moduleLoader interfaces are supported. No Webpack plugin, no Vite plugin, no bundler-specific manifest generation. Consumers wire up their own resolution logic, which makes this library usable with any bundler or runtime.

No runtime-specific hooks

No Node.js ESM loader hooks, no CJS require hooks, no environment-specific registration. The consumer is responsible for handling "use client" / "use server" directive detection in their build tool or runtime.


Comparison with react-server-dom-webpack

| Capability | react-server-dom-webpack | @lazarv/rsc | |---|---|---| | Flight protocol | ✅ | ✅ Full parity | | Bundler | Webpack only | Any (abstract interface) | | Runtime | Node.js (+ browser client) | Any Web Platform runtime | | renderToReadableStream | ✅ | ✅ | | renderToPipeableStream | ✅ (Node.js) | — (use ReadableStream) | | createFromReadableStream | ✅ | ✅ | | createFromNodeStream | ✅ (Node.js) | — (use ReadableStream) | | encodeReply / decodeReply | ✅ | ✅ | | Temporary references | ✅ | ✅ | | Bound actions (.bind()) | ✅ | ✅ | | Error digest propagation | ✅ | ✅ | | Synchronous thenable (use()) | ✅ | ✅ | | Webpack plugin / manifest | ✅ | — (by design) | | Node ESM loader hooks | ✅ | — (by design) | | react-server condition gating | ✅ | — | | Prerender | ✅ | ✅ |


Related


License

MIT