Examples

Static Site (GitHub Pages)

Minimal config for a static site deployed to GitHub Pages:

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

export default defineConfig({
  base: "/my-repo/",
  plugins: vitePluginReactServer({
    moduleBase: "src",
    moduleBaseURL: "/my-repo/",
    Page: (url) => `src/pages${url}page.tsx`,
    props: (url) => `src/pages${url}props.ts`,
    Html: "src/Html.tsx",
    build: { pages: ["/", "/about/"] },
  }),
});

Build and deploy dist/static/.

Live examples:

Dynamic Server (Express)

Use the build output as ESM modules in an Express server:

// server.ts
import express from "express";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const app = express();

// Static pages (pre-rendered)
app.use(express.static(join(__dirname, "dist/static")));

// RSC endpoint
app.get("*.rsc", (req, res) => {
  res.setHeader("Content-Type", "text/x-component");
  res.sendFile(join(__dirname, "dist/static", req.path));
});

// Dynamic route — render on request using server modules
app.get("/user/:id", async (req, res) => {
  const { Page } = await import("./dist/server/pages/user/page.js");
  const props = { userId: req.params.id };
  // Use createRscStream / createHtmlStream to render
  // ...
});

app.listen(3000);

Server Actions

// src/actions/submit.server.ts
"use server";

export async function submitForm(data: FormData) {
  const name = data.get("name") as string;
  // Save to database, send email, etc.
  return { success: true, name };
}
// src/pages/contact/page.tsx
import { submitForm } from "../../actions/submit.server.js";

export const Page = () => (
  <form action={submitForm}>
    <input name="name" required />
    <button type="submit">Submit</button>
  </form>
);

Custom Routing

The plugin doesn't own routing. The Page option is a simple URL-to-file mapper:

// File-based routing
Page: (url) => `src/pages${url}page.tsx`,

// Single page
Page: "src/page.tsx",

// Custom mapping
Page: (url) => {
  const routes: Record<string, string> = {
    "/": "src/home.tsx",
    "/about/": "src/about.tsx",
    "/blog/": "src/blog/index.tsx",
  };
  return routes[url] || "src/404.tsx";
},

Bring your own client-side router (React Router, TanStack Router, etc.) for navigation.

React in Config (Server-First)

With react-server condition, you can use JSX directly in your Vite config:

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

export default defineConfig({
  plugins: vitePluginReactServer({
    moduleBase: "src",
    Page: "src/page.tsx",
    components: {
      Html: ({ Root, cssFiles, globalCss, pageProps, Page }) => (
        <html>
          <head><Css cssFiles={globalCss} /></head>
          <body>
            <Root as="div" id="root" cssFiles={cssFiles} Page={Page} pageProps={pageProps} />
          </body>
        </html>
      ),
    },
    build: { pages: ["/"] },
  }),
});

Custom HTML Shell

// src/Html.tsx
import { Css } from "vite-plugin-react-server/components";
import type { HtmlProps } from "vite-plugin-react-server/types";

export const Html = ({ Root, cssFiles, globalCss, pageProps = { title: "My App" }, Page }: HtmlProps) => (
  <html lang="en">
    <head>
      <meta charSet="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <Css cssFiles={globalCss} />
      <title>{pageProps.title}</title>
    </head>
    <body>
      <Root as="main" id="root" cssFiles={cssFiles} Page={Page} pageProps={pageProps} />
    </body>
  </html>
);

Custom Root Component

// src/Root.tsx
import React from "react";
import { Css } from "vite-plugin-react-server/components";

export const Root = ({ Page, pageProps = {}, as: As = React.Fragment, cssFiles, ...props }) => {
  if (As === React.Fragment) {
    return <><Page {...pageProps} /></>;
  }
  return (
    <As {...props} role="main">
      <Page {...pageProps} />
      <Css cssFiles={cssFiles} />
    </As>
  );
};