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

vite-plugin-react-server

v3.1.2

Published

Vite plugin for React Server Components (RSC)

Readme

vite-plugin-react-server

React Server Components as a Vite plugin. One vite build --app prerenders your pages to static HTML + RSC payloads and emits your components as portable ESM that runs under any HTTP server: static hosting, Express/Hono, or anything in between.

📖 Documentation site → — the full docs, and itself a vprs app (the site dogfoods the plugin).

vprs is the low-level layer rather than a framework: it handles the RSC transform, runs the worker threads, and emits portable ESM — and leaves routing and app structure to you. Use it directly, or as the engine under your own conventions. Its closest peer is the official @vitejs/plugin-rsc; vprs differs by being a small dev/build setup whose output is portable ESM you host yourself. (The RSC transport underneath is an implementation detail — supplied and version-locked by react-server-loader.) For a batteries-included framework instead, see Waku or Vike. Full breakdown: How vprs compares.

It runs in both Node module conditions by design: the dev server and the build work with or without --conditions react-server, and a worker thread mirrors whichever half your main thread isn't on (server components need a react-server context, client hydration a react-client one). Running the main thread under react-server is an optional optimization — a bit faster, better stack traces — never a requirement.

Install

npm install -D vite-plugin-react-server react react-dom

vprs runs on stable React 19.2+ out of the box — and on experimental React too. Everything locked to a React version (the react-server-dom-esm transport that ships on both the server and your browser bundle, the directive engine, the Node loader) lives in the react-server-loader dependency, whose versions track React the way @types/react does. You don't build or manage a transport — you pick a React track and install the matching react-server-loader. The command above is all you need for stable.

To run the experimental train instead, install the three together; the react-server-loader range collapses them to one copy, so no overrides are needed:

npm install react@experimental react-dom@experimental react-server-loader@experimental

Experimental buys the newest RSC features ahead of stable — for instance it already fixes the cosmetic as="stylesheet" CSS-preload warning that stable React 19.2.x logs. See React Compatibility; upgrading from 1.x, see the migration notes.

Minimal Example

// vite.config.ts
import { defineConfig } from "vite";
import { vitePluginReactServer } from "vite-plugin-react-server";

export default defineConfig({
  plugins: vitePluginReactServer({
    moduleBase: "src",
    Page: "src/page.tsx",
    build: { pages: ["/"] },
  }),
});
// src/page.tsx
export const Page = ({ url }: { url: string }) => <div>Hello from {url}</div>;
# Dev server
npx vite

# Build (static site + server/client ESM)
npx vite build --app

# Same build, react-server main thread — optional: a bit faster, better stack traces
NODE_OPTIONS='--conditions react-server' vite build --app

Build Output

dist/
├── static/          # Deployable to any static host
│   ├── index.html   # Pre-rendered HTML
│   └── index.rsc    # RSC payload for client navigation
├── client/          # Client-side ESM modules (for SSR)
└── server/          # Server-side ESM modules (with server actions)

dist/static/ is a complete static site. dist/client/ and dist/server/ are ESM modules you can import in your own Express/Hono/Node server.

Client components

vprs recognises a file as a client module when either of these is true:

  • the filename matches (^|[\/.])client\.[cm]?[jt]sx?$ — i.e. Button.client.tsx, bar.client.mjs, or the standalone basename src/client.tsx / client.tsx, or
  • the file starts with a top-of-file "use client" directive (leading whitespace, line/block comments, and an optional "use strict" prologue are tolerated above it).

Either is sufficient. Substrings like clientUtils.tsx, clientId.ts, or clients.tsx are not treated as client modules, and a "use client" directive placed after real code does not count.

// src/components/Counter.tsx  ← no `.client.` suffix needed
"use client";
import { useState } from "react";
export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

See Getting Started.

Third-party client-component packages

Component libraries like Chakra UI, MUI, Mantine, react-aria, and framer-motion are client-only — their components rely on React context/state and must run inside a client boundary, the same constraint they carry under Next.js's App Router. Use them within a "use client" component (commonly a small provider wrapper); they can't be imported directly into a server component. This isn't a vprs limitation — e.g. Chakra's own Next.js App Router guide requires wrapping ChakraProvider in a 'use client' component.

vprs auto-detects these so they're treated correctly at build start: any package with react in its peerDependencies is classified as a client package (using vitefu.crawlFrameworkPkgs). Two escape hatches if needed:

vitePluginReactServer({
  // Force a package into the list (e.g. one that doesn't peerDep react)
  clientPackages: ["@my/internal-ui"],
  // Skip a detected one (e.g. devDeps Storybook bringing along @storybook/react)
  excludeClientPackages: ["@storybook/react", "@storybook/react-vite"],
});

Storybook

vprs ships a Storybook preset — add one line and your RSC app's components build and render in Storybook:

// .storybook/main.ts
export default {
  framework: { name: "@storybook/react-vite", options: {} },
  addons: ["vite-plugin-react-server/storybook"],
};

It strips the vprs plugin from Storybook's builder, resolves the react-server-dom-esm transport (from react-server-loader), and silences "use client"/"use server" directive noise. See Storybook for details.

Documentation

Everything below is also published as a browsable site at nicobrinkkemper.github.io/vite-plugin-react-server, which is itself built with vprs.

| Doc | What it covers | |-----|---------------| | How vprs compares | vprs vs @vitejs/plugin-rsc, Waku, Vike — and what vprs does not do | | Getting Started | Install → first page → dev server → build → deploy | | Storybook | One-line Storybook support for vprs apps | | Build Output | What the build produces, how to use the ESM modules | | Configuration | All plugin options | | CSS Handling | Inline/linked CSS, CSS modules, the Css component | | Server Actions | "use server" directives, form actions, hosting | | Examples | Static site, dynamic server, server actions, custom routing | | Troubleshooting | Common errors and fixes | | API Reference | Exported functions, types, and components |

Internals (contributors)

| Doc | What it covers | |-----|---------------| | Architecture | Condition system, module structure, plugin composition | | Transformer | How "use client" / "use server" directives are processed | | Workers | RSC and HTML worker threads |

Maintenance

| Doc | What it covers | |-----|---------------| | Releasing | Version bumps, publishing, demo updates | | React Compatibility | Vendored ESM transport, type system |

Requirements

  • Node.js 22.0.0+ (the build uses node:fs/promises#glob, which landed in 22)
  • React 19.2+, stable (react / react-dom at ^19.2.7) or experimental. The RSC server APIs vprs uses (prerenderToNodeStream, the react-server transport exports) ship in stable React; the matching react-server-dom-esm transport comes from the react-server-loader dependency, which tracks your React track. See React Compatibility.
  • Vite 6, 7, or 8 (vite peer: ^6.3.5 || ^7 || ^8). Vite 8 builds with Rolldown/Oxc instead of Rollup/esbuild; vprs runs on all three.

TypeScript

{
  "compilerOptions": {
    "types": ["vite/client", "vite-plugin-react-server/virtual"]
  }
}

License

MIT