react-server675fbba4
react-serverfilesdocssrcpagesen(pages)integrationsreact.mdx
docs/src/pages/en/(pages)/integrations/react.mdxmdx10.2 KiB82abf189

title: React category: Integrations order: 3

import Link from "../../../../components/Link.jsx";

React

@lazarv/react-server ships with React built-in. You do not need to install react or react-dom yourself — the runtime includes the correct experimental version of React that supports React Server Components, server actions, and all the latest React features.

<Link name="why-react-is-built-in"> ## Why React is built-in </Link>

React Server Components require an experimental build of React that includes the RSC wire protocol, server references, and streaming support. These experimental APIs are not available in the stable releases published to npm. By bundling the exact React version it was built against, @lazarv/react-server guarantees:

  • Correct RSC support — the react-server export condition, @lazarv/rsc serialization layer, and streaming primitives all match.
  • Zero configuration — you don't need to figure out which experimental React build to install, or keep it in sync across react, react-dom, and the RSC serialization layer.
  • Single React instance — the runtime ensures only one copy of React is loaded at any time, avoiding the notorious "multiple React instances" bugs (broken hooks, broken context, etc.).
<Link name="no-react-in-your-package-json"> ## No React in your package.json </Link>

When you create a project with @lazarv/react-server, your package.json should not list react or react-dom as dependencies:

{
  "dependencies": {
    "@lazarv/react-server": "latest"
  }
}

That's it. The runtime provides React for your entire project — server components, client components, and SSR.

If you are migrating from an existing React project, remove react and react-dom from your dependencies:

pnpm remove react react-dom
<Link name="how-it-works"> ## How it works </Link>

@lazarv/react-server lists react and react-dom as direct dependencies (not peer dependencies) and uses @lazarv/rsc — a bundler-agnostic RSC serialization layer — instead of react-server-dom-webpack. At both development and build time, the runtime sets up module aliases so that any import of react or react-dom — whether from your code or from third-party libraries — resolves to the React version bundled with the runtime.

This aliasing operates at the Vite module resolution level across all three environments:

  • rsc — React Server Components use the react-server export condition (react.react-server.js)
  • ssr — server-side rendering uses the standard React entry (index.js)
  • client — client components in the browser use the same React version, bundled by Vite

Because the aliasing intercepts resolution before any bundling happens, third-party libraries that import from react or react-dom will transparently use the runtime's React — no configuration needed.

<Link name="third-party-library-compatibility"> ## Third-party library compatibility </Link>

Most React libraries work out of the box with @lazarv/react-server. The key compatibility considerations are:

Libraries that work seamlessly

Any library that depends on react or react-dom via standard imports will work, because module aliasing ensures it receives the runtime's React. This includes:

  • UI component librariesMaterial UI, Mantine, Chakra UI, Radix, shadcn/ui, Headless UI, etc.
  • State management — Zustand, Jotai, Recoil, Redux (in client components)
  • Data fetchingTanStack Query, SWR, Apollo Client (in client components)
  • Animation — Framer Motion, React Spring
  • Forms — React Hook Form, Formik (in client components)
  • Styling — Emotion, Styled Components, Tailwind CSS, CSS Modules

Client vs. server components

Third-party libraries that use React hooks (useState, useEffect, useRef, etc.) or browser APIs must be used inside client components (files with "use client" directive). This is a React Server Components rule, not a @lazarv/react-server limitation.

"use client";

import { useState } from "react";
import { motion } from "framer-motion";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <motion.button
      whileTap={{ scale: 0.95 }}
      onClick={() => setCount(count + 1)}
    >
      count is {count}
    </motion.button>
  );
}

Server components can import and render client components, but cannot use hooks or browser APIs themselves. See the client components guide and server components guide for more details.

Libraries with React version checks

Some libraries perform runtime version checks (e.g., reading React.version). Since @lazarv/react-server uses an experimental React build, the version string looks like 0.0.0-experimental-... rather than a stable 19.x.x. Most libraries handle this gracefully, but in rare cases a library might warn or refuse to load because it doesn't recognize the version.

If you encounter this, the library usually still works correctly — the warning can be safely ignored. If a library strictly blocks on version, check whether a newer version of that library has relaxed its version check, or open an issue with the library maintainer.

Libraries that bundle their own React

Occasionally, a library might bundle its own copy of React instead of importing it as a peer dependency. This can cause the "multiple React instances" problem — hooks errors (Invalid hook call) or context not being shared. However, @lazarv/react-server already deduplicates react and react-dom automatically, so most of these cases are handled for you without any extra configuration.

<Link name="react-compiler"> ## React Compiler </Link>

React Compiler is an opt-in Babel plugin that automatically memoizes React components and hooks. It removes most of the manual useMemo, useCallback, and React.memo boilerplate by analyzing your code at build time and emitting useMemoCache calls for the values that need to be cached.

Because @lazarv/react-server already uses @vitejs/plugin-react internally to apply the React Babel transform, enabling React Compiler is a matter of providing your own @vitejs/plugin-react instance with babel-plugin-react-compiler configured. When the runtime detects a user-supplied vite:react plugin in your config, it uses yours in place of the built-in one — so the runtime's React aliasing, JSX transform, and Fast Refresh continue to work exactly as before.

Install

pnpm add -D @vitejs/plugin-react babel-plugin-react-compiler

You do not need to install react or react-dom — see No React in your package.json.

Configure

Add @vitejs/plugin-react with babel-plugin-react-compiler to your react-server.config.mjs:

import react from "@vitejs/plugin-react";

export default {
  plugins: [
    react({
      babel: {
        plugins: [
          [
            "babel-plugin-react-compiler",
            {
              target: "19",
            },
          ],
        ],
      },
    }),
  ],
};

The target: "19" option tells React Compiler to emit calls to React 19's built-in useMemoCache hook, which the React build that ships with @lazarv/react-server supports natively. No react-compiler-runtime polyfill is required.

Verify it is running

After a build (react-server build), inspect a compiled client component bundle in .react-server/client/. Compiled components contain a call like c(N) (the useMemoCache cache) and Symbol.for("react.memo_cache_sentinel") slot initializers. If you see those, the compiler is active.

Compilation modes

By default, React Compiler runs in infer mode and tries to memoize every component it can prove safe. To opt in selectively instead, set compilationMode: "annotation" and add a "use memo" directive to the components or hooks you want compiled:

react({
  babel: {
    plugins: [
      [
        "babel-plugin-react-compiler",
        {
          target: "19",
          compilationMode: "annotation",
        },
      ],
    ],
  },
}),
"use client";

function Chart({ data }) {
  "use memo";
  // …only this component is compiled.
}

Server vs. client components

The Babel transform runs across all three environments (rsc, ssr, and client), so React Compiler may compile both server and client components. Only client components benefit from re-render memoization in the browser; server components are still useful to compile because the same transform runs during SSR. If a particular file should not be compiled, exclude it via the standard babel-plugin-react-compiler options (sources filter or "use no memo" directive).

For server functions ("use server") the compiler is a no-op — they are not React components and the compiler ignores them by structure.

Example

A complete working setup is available in the react-compiler example.

<Link name="typescript-types"> ## TypeScript types </Link>

For TypeScript users, you should use the experimental React types in your tsconfig.json:

{
  "compilerOptions": {
    "types": ["react/experimental", "react-dom/experimental"]
  }
}

This ensures your editor recognizes the latest React APIs like "use client", "use server", useActionState, useFormStatus, and other experimental features. See the TypeScript integration guide for a complete configuration example.

<Link name="checking-the-react-version"> ## Checking the React version </Link>

To see which React version @lazarv/react-server is using, you can check it at runtime:

import { version } from "react";

export default function Version() {
  return <p>React version: {version}</p>;
}

Or inspect the installed version via the CLI:

node -e "console.log(require('@lazarv/react-server/node_modules/react/package.json').version)"